Да и нет.Удаление записи безопасно, поскольку запись не будет существовать после ее удаления, но небезопасно предполагать, что вы не попадете в этот индекс после удаления, пока цикл повторяется.
Спецификация POSIX не может сказать:
the following code deletes an entire array:
for (index in array)
delete array[index]
, если это может пропустить индекс, а это:
for (index in arrayA) {
if (index in arrayB) {
print "Both:", index
delete arrayA[index]
delete arrayB[index]
}
}
for (index in arrayA)
print "A only:", index
for (index in arrayB)
print "B only:", index
- это чрезвычайно общая идиома для определения, в каких наборах значений есть значения, и которые не сработали бы, если бы этот подход не был «безопасным» в этом контексте.
НО , что не означает, что вы можетеПредположим, что индекс массива не будет достигнут после того, как он был удален во время цикла , потому что вычисляет ли awk все индексы массива, которые будут посещены до входа в цикл или во время выполнения, зависит от реализации.Например, GNU awk определяет все индексы, которые он посетит до ввода цикла , поэтому вы получаете такое поведение, когда массив становится на 1 элемент короче после delete a[3]
, но удаленный индекс 3
по-прежнемупосещенный в цикле, где он был ранее удален:
$ gawk 'BEGIN{split("a b c d e",a);
for (i in a) {print length(a), i, a[i]; delete a[3]} }'
5 1 a
4 2 b
4 3
4 4 d
4 5 e
, но не все awk делают это, например, BWK awk / nawk не делает и не делает MacOS / BSD awk:
$ awk 'BEGIN{split("a b c d e",a);
for (i in a) {print length(a), i, a[i]; delete a[3]} }'
5 2 b
4 4 d
4 5 e
4 1 a
Поведение gawk эквивалентно этому в других упомянутых выше awk:
$ awk 'BEGIN{split("a b c d e",a); for (i in a) b[i];
for (i in b) { print length(a), i, (i in a ? a[i] : x); delete a[3]} }'
5 2 b
4 3
4 4 d
4 5 e
4 1 a
Я использую неназначенную переменную x
выше вместо ""
, чтобы точно изобразить ноль или ноль природыa[3]
после удаления, но на самом деле это не имеет значения, так как мы печатаем его как "" в любом случае.
Так что, независимо от того, какой awk вы используете, после завершения вышеуказанного цикла a[3]
пропадет, например, с GNU awk снова:
$ gawk 'BEGIN{split("a b c d e",a);
for (i in a) {print length(a), i, a[i]; delete a[3]}
print "---";
for (i in a) {print i, a[i]} }'
5 1 a
4 2 b
4 3
4 4 d
4 5 e
---
1 a
2 b
4 d
5 e
Обратите внимание, что в этом сценарии выше a[3]
фактически воссоздается во время первого цикла из-за доступа к a[i]
, когда i
равен 3
, нотогда случается delete a[3]
g для каждого индекса - это то, что удаляет его снова.Если бы мы выполняли удаление только тогда, когда i
равно 1
, мы бы увидели, что a[3]
существует, но содержит ноль или ноль после цикла:
$ gawk 'BEGIN{split("a b c d e",a);
for (i in a) {print length(a), i, a[i]; if (i==1) delete a[3]}
print "---";
for (i in a) {print i, a[i]} }'
5 1 a
4 2 b
4 3
5 4 d
5 5 e
---
1 a
2 b
3
4 d
5 e
Чтобы понять, почему подход gawk кПредварительное определение индексов, которые будут посещаться до того, как вы начнете зацикливание, лучше, чем попытка определить их на лету во время зацикливания, рассмотрим следующий код, который пытается добавить 3 новых элемента в массив внутри цикла:
$ cat tst.awk
BEGIN {
split("a b c",a)
for (i in a) {
j=i+100
a[j] = "foo" j
print length(a), i, a[i]
}
print "---"
for (i in a) {
print i, a[i]
}
}
С gawk выходной и конечный результат предсказуемы и по желанию:
$ gawk -f tst.awk
4 1 a
5 2 b
6 3 c
---
6 1 a
6 2 b
6 3 c
6 101 foo101
6 102 foo102
6 103 foo103
, а в MacOS / BSD awk (игнорируйте порядок, просто посмотрите на длину массива изначения индексов):
$ awk -f tst.awk
4 2 b
5 3 c
6 102 foo102
7 103 foo103
8 202 foo202
9 203 foo203
10 302 foo302
11 1 a
---
11 303 foo303
11 2 b
11 3 c
11 402 foo402
11 101 foo101
11 102 foo102
11 103 foo103
11 202 foo202
11 203 foo203
11 302 foo302
11 1 a
это, по-видимому, хаотично, потому что он пытается получить доступ к индексам, добавляемым в цикл во время его цикла, но с ограниченным успехом (предположительно, из-за порядка новых индексов в хэшетаблица против ранее посещенных записей хеш-таблицы), что повезло, иначе мы застряли бы в бесконечном цикле.
Чтобы получить полезные результаты от MacOS / BSD awk и т. д. вам снова нужно сохранить предопределенные индексы в новом массиве перед циклом, как уже показано выше:
$ cat tst.awk
BEGIN {
split("a b c",a)
for (i in a) {
b[i]
}
for (i in b) {
j=i+100
a[j] = "foo" j
print length(a), i, a[i]
}
print "---"
for (i in a) {
print length(a), i, a[i]
}
}
$ awk -f tst.awk
4 2 b
5 3 c
6 1 a
---
6 2 b
6 3 c
6 101 foo101
6 102 foo102
6 103 foo103
6 1 a
Oh и wrt I know we don't have much control on the order in which the array elements are scanned
- с GNU awk вы можете точно контролировать это, установив PROCINFO["sorted_in"]
, см. https://www.gnu.org/software/gawk/manual/gawk.html#Controlling-Scanning. Например:
$ gawk 'BEGIN{split("10 2 bob alf",a);
PROCINFO["sorted_in"]="@ind_str_asc"; for (i in a) print i, a[i]}'
1 10
2 2
3 bob
4 alf
$ gawk 'BEGIN{split("10 2 bob alf",a);
PROCINFO["sorted_in"]="@val_str_asc"; for (i in a) print i, a[i]}'
1 10
2 2
4 alf
3 bob
$ gawk 'BEGIN{split("10 2 bob alf",a);
PROCINFO["sorted_in"]="@val_num_asc"; for (i in a) print i, a[i]}'
4 alf
3 bob
2 2
1 10