【问题标题】:Why does awk seem to randomize the array?为什么 awk 似乎随机化了数组?
【发布时间】:2014-01-08 03:02:05
【问题描述】:

如果您查看此awk 测试的输出,您会发现awk 中的array 似乎以某种随机模式打印。对于相同数量的输入,它似乎是相同的顺序。为什么会这样?

echo "one two three four five six" | awk '{for (i=1;i<=NF;i++) a[i]=$i} END {for (j in a) print j,a[j]}'
4 four
5 five
6 six
1 one
2 two
3 three

echo "P04637 1A1U 1AIE 1C26 1DT7 1GZH 1H26 1HS5 1JSP 1KZY 1MA3 1OLG 1OLH 1PES 1PET 1SAE 1SAF 1SAK 1SAL 1TSR 1TUP 1UOL 1XQH 1YC5 1YCQ" | awk '{for (i=1;i<=NF;i++) a[i]=$i} END {for (j in a) print j,a[j]}'
17 1SAF
4 1C26
18 1SAK
5 1DT7
19 1SAL
6 1GZH
7 1H26
8 1HS5
9 1JSP
10 1KZY
20 1TSR
11 1MA3
21 1TUP
12 1OLG
22 1UOL
13 1OLH
23 1XQH
14 1PES
1 P04637
24 1YC5
15 1PET
2 1A1U
25 1YCQ
16 1SAE
3 1AIE

为什么要这样做,有没有规定?

【问题讨论】:

  • 如果你想得到一个固定的订单,不要使用x in y。使用for/while,因为x in y 不会保持原来的顺序。

标签: arrays awk


【解决方案1】:

参考for (value in array) 语法时,来自 GNU Awk 用户指南中的 8. Arrays in awk --> 8.5 Scanning All Elements of an Array

this 访问数组元素的顺序 语句由数组的内部排列决定 awk 中的元素,无法控制或更改。这可能导致 如果新元素被添加到数组中的语句中的问题 循环体;无法预测 for 循环是否会 到达他们。同样,在循环内更改 var 可能会产生 奇怪的结果。最好避免这样的事情。


所以如果你想按照你存储的顺序打印数组,那么你必须使用经典的for循环:

for (j=1; j<=NF; j++) print j,a[j]

示例:

$ awk '{for (i=1;i<=NF;i++) a[i]=$i} END {for (j=1; j<=NF; j++) print j,a[j]}' <<< "P04637 1A1U 1AIE 1C26 1DT7 1GZH 1H26 1HS5 1JSP 1KZY 1MA3 1OLG 1OLH 1PES 1PET 1SAE 1SAF 1SAK 1SAL 1TSR 1TUP 1UOL 1XQH 1YC5 1YCQ"
1 P04637
2 1A1U
3 1AIE
4 1C26
5 1DT7
6 1GZH
7 1H26
8 1HS5
9 1JSP
10 1KZY
11 1MA3
12 1OLG
13 1OLH
14 1PES
15 1PET
16 1SAE
17 1SAF
18 1SAK
19 1SAL
20 1TSR
21 1TUP
22 1UOL
23 1XQH
24 1YC5
25 1YCQ

【讨论】:

  • 代码和参考都很好,但这并不能解释原因,这是 Jotne 所问的。
  • @Jotne 绝对不是,微妙的是真的不符合value in arrayorder。如果顺序无关紧要,这是一个非常好的结构。
  • 我想我错过了一条 Jotne 评论。好吧,当然,for (i in a) 在许多情况下都非常有用。如果订单很重要,那就没用了。
  • @fedorqui Ops 意外删除了它 :) 问题是 id 我应该避免使用 value in array
  • 没问题,@Jotne :) 正如 sudo_O 巧妙地说的那样,你不应该这样做。这是一个非常有用的工具,您只需要在订单很重要时避免使用它。
【解决方案2】:

Awk 使用hash tables 来实现关联数组。这只是这种特定数据结构的固有属性。特定元素存储到数组中的位置取决于值的哈希值。其他要考虑的因素是哈希表的实现。如果它具有内存效率,它将使用模数函数或其他方法限制每个键的存储范围。您还可能会得到不同键的冲突哈希值,因此会发生链接,再次影响顺序,具体取决于首先插入的键。

(key in array) 构造在适当用于循环每个键时非常好,但您不能指望顺序,并且您不应该在循环中更新array,因为您可能会多次处理array[key]错误。

Think Complexity 一书中对哈希表有很好的描述。

【讨论】:

    【解决方案3】:

    问题在于您用于获取数组索引的运算符,而不是数组存储在哈希表中的事实。

    in 运算符以随机(外观)顺序提供数组索引(默认情况下与哈希表相关,但这是一种实现选择,可以修改)。

    for 循环以数字递增的顺序显式提供数组索引,该循环也在 in 运算符所在的同一个哈希表上运行,但无论如何都会以特定顺序产生输出。

    这只是获取数组索引的两种不同方式,这两种方式都适用于哈希表。

    man awk 并查找 in 运算符。

    如果您想使用 in 运算符控制输出顺序,您可以使用 GNU awk(从 4.0 版开始)通过填充 PROCINFO["sorted_in"] 来实现。详情请见http://www.gnu.org/software/gawk/manual/gawk.html#Controlling-Array-Traversal

    访问数组索引的一些常用方法:

    以您不关心的顺序打印数组元素:

    {a[$1]=$0} END{for (i in a) print i, a[i]}
    

    如果索引是数字且从 1 开始连续,则按索引的数字顺序打印数组元素:

    {a[++i]=$0} END{for (i=1;i in a;i++) print i, a[i]}
    

    如果索引是数字但不连续,则按索引的数字顺序打印数组元素:

    {a[$1]=$0; min=($1<min?$1:min); max=($1>max?$1:max)} END{for (i=min;i<=max;i++) if (i in a) print i, a[i]}
    

    按照在输入中看到的顺序打印数组元素:

    {a[$1]=$0; b[++max]=$1} END{for (i=1;i <= max;i++) print b[i], a[b[i]]}
    

    使用 gawk 4.0+ 以特定的索引顺序打印数组元素:

    BEGIN{PROCINFO["sorted_in"]=whatever} {a[$1]=$0} END{for (i in a) print i, a[i]}
    

    对于其他任何事情,编写您自己的代码和/或查看 gawk asort()asorti()

    【讨论】:

    • 很棒的概述。您是否使用in a 代替i&lt;=length(a) 作为(i=1;i in a;i++) 的一部分,因为POSIX 规范不支持数组的length() 函数,或者这样做还有其他优势吗?回复 whateverPROCINFO["sorted_in"] 支持的值为 "@ind_str_asc""@ind_num_asc""@val_type_asc""@val_str_asc""@val_num_asc""@ind_str_desc""@ind_num_desc"、@9876545349@、@97654349@、@980 @,和"@unsorted",或者自定义比较函数的名称。
    • 是的,我使用 in a 的时间比 gawk 支持 length(a) 的时间长,所以我已经习惯了它,它便于携带,而且为了提高效率(和美观)我讨厌打电话的想法具有相同参数的相同函数通过循环每次迭代产生相同的结果。如果我不打算使用in a,那么我会在循环之外设置一个变量并对其进行测试。谢谢。
    • 知道了,谢谢,但是在每个迭代中调用in a实际上比调用length(a)更有效吗?无论哪种方式,我确实明白了将数组大小缓存在前面的变量中。
    • 我不得不认为in alength(a) 更有效,因为in a 只是一个内置的哈希查找,而length(a) 是一个函数调用并计算元素的数量大批。所以现在我受到启发去测试它。每个脚本在 1,000,000 个元素的数组上运行 3 次后,inlength 稍快,但接近:0m0.421s vs 0m0.437s
    【解决方案4】:

    如果您使用的是 gawk 或 mawk,您还可以设置一个环境变量 WHINY_USERS,它将在迭代之前对索引进行排序。

    例子:

    echo "one two three four five six" | WHINY_USERS=true awk '{for (i=1;i<=NF;i++) a[i]=$i} END {for (j in a) print j,a[j]}'
    1 one
    2 two
    3 three
    4 four
    5 five
    6 six
    

    来自mawk的手册:

    WHINY_USERS

    这是一个未记录的 gawk 功能。它告诉 mawk 在开始迭代数组元素之前对数组索引进行排序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-02
      • 1970-01-01
      • 2016-02-25
      • 1970-01-01
      • 2019-06-21
      • 1970-01-01
      • 1970-01-01
      • 2021-12-27
      相关资源
      最近更新 更多