awk '!a [$ 0] ++'はどのように機能しますか?


39

このワンライナーは、事前ソートなしでテキスト入力から重複行を削除します。

例えば:

$ cat >f
q
w
e
w
r
$ awk '!a[$0]++' <f
q
w
e
r
$ 

インターネットで見つけた元のコードは次のとおりです。

awk '!_[$0]++'

_Perlのようにawkで特別な意味を持つようになったので、これはさらに困惑しましたが、それは単なる配列の名前であることが判明しました。

これで、ワンライナーの背後にあるロジックがわかりました。 各入力行はハッシュ配列のキーとして使用されるため、完了すると、ハッシュには到着順に一意の行が含まれます。

私が学びたいのは、この表記がawkによってどのように解釈されるかです。たとえば、バング記号(!)の意味とこのコードスニペットの他の要素。

どのように機能しますか?


タイトルは誤解を招きやすいため、$ o(o)ではなく$ 0(ゼロ)にする必要があります。
アルケマール14年

2
ハッシュであるため、順序付けられていないため、「到着順に」は実際には正しくありません。
ケビン14年

回答:


35

どれどれ、

 !a[$0]++

最初

 a[$0]

a[$0]a入力行全体($0)をキーとする配列)の値を確認します。

存在しない場合(!テストの否定はtrueに評価されます)

 !a[$0]

入力行を印刷します$0(デフォルトのアクション)。

また、に1(++)を追加するa[$0]ため、次回!a[$0]はfalseと評価されます。

いいね!あなたはコードゴルフを見るべきです!


1
本質はこれです:単一引用符で囲まれた式は、awk各入力行のテストとして使用されます。テストが成功awkするたびに、中括弧でアクションを実行します{print}。省略した場合はです。ありがとう!
アレクサンダーシュチェブリキン14年

3
@Archemar:この答えは間違っています。
cuonglm 14年

@AlexanderShcheblikin awkでは、デフォルトのアクションは{print $0}です。これは、trueと評価されたものはすべてこれをデフォルトとして実行することを意味します。したがって、たとえばawk '1' file、すべての行を印刷しawk '$1' file、その最初のフィールド空または0されていないすべてのこれらのライン、等を印刷
fedorqui

6
@Gnoucこの回答には重大なエラーはありません。それがあなたが参照しているものである場合、式の値が計算された後に増分が実際に適用されます。増分は印刷の前に行われるのは事実ですが、それは基本的な説明に影響を及ぼさないわずかな不正確さです。
ジル「SO-悪であるのをやめる」14年

1
:私は初心者がQuoraの中で、ここで理解するための最良の説明を見つけqr.ae/TUIVxM
GP92

29

処理は次のとおりです。

  • a[$0]$0連想配列のキーの値を見てくださいa。存在しない場合は作成します。

  • a[$0]++:の値をインクリメントし、a[$0]式の値として古い値を返します。a[$0]存在しない場合は、戻り0、増分a[$0]します1++演算子は数値を返します)。

  • !a[$0]++:式の値を否定します。a[$0]++returnの場合0、式全体がtrueと評価され、awk実行されたデフォルトアクションが実行されますprint $0。そうでない場合、式全体が偽と評価され、awk何も起こりません。

参照:

ではgawk、我々は使用することができます(またはdgawk awk --debug新しいバージョンで)デバッグするgawkスクリプトを。まず、gawkという名前のスクリプトを作成しますtest.awk

BEGIN {                                                                         
    a = 0;                                                                      
    !a++;                                                                       
}

次に実行します:

dgawk -f test.awk

または:

gawk --debug -f test.awk

デバッガーコンソールで:

$ dgawk -f test.awk
dgawk> trace on
dgawk> watch a
Watchpoint 1: a
dgawk> run
Starting program: 
[     1:0x7fe59154cfe0] Op_rule             : [in_rule = BEGIN] [source_file = test.awk]
[     2:0x7fe59154bf80] Op_push_i           : 0 [PERM|NUMCUR|NUMBER]
[     2:0x7fe59154bf20] Op_store_var        : a [do_reference = FALSE]
[     3:0x7fe59154bf60] Op_push_lhs         : a [do_reference = TRUE]
Stopping in BEGIN ...
Watchpoint 1: a
  Old value: untyped variable
  New value: 0
main() at `test.awk':3
3           !a++;
dgawk> step
[     3:0x7fe59154bfc0] Op_postincrement    : 
[     3:0x7fe59154bf40] Op_not              : 
Watchpoint 1: a
  Old value: 0
  New value: 1
main() at `test.awk':3
3           !a++;
dgawk>

あなたが見ることができる、Op_postincrement前に実行されましたOp_not

siまたはのstepi代わりにsまたはstepを使用して、より明確に表示することもできます。

dgawk> si
[     3:0x7ff061ac1fc0] Op_postincrement    : 
3           !a++;
dgawk> si
[     3:0x7ff061ac1f40] Op_not              : 
Watchpoint 1: a
  Old value: 0
  New value: 1
main() at `test.awk':3
3           !a++;

3
@Archemar:あなたの答えは、!が前に適用されることを示してい++ます。
cuonglm 14年

6
この答えは間違っています。インクリメントは、!演算子の結果が計算された後に発生します。演算子の優先順位(の!a[$0]++ように解析される!(a[$0]++))と評価の順序(a[$0]式の値が計算された後に新しい値の割り当てが発生する)を混同しています。
ジル 'SO-悪である停止' 14

5
@Gnoucあなたが引用した箇所で正しいことを言っており、もしそれがあなたが説明した方法で機能していたら、このコードは望みどおりの効果をもたらさないでしょう。最初に値!xが計算されます。ここでxはの古い値ですa[$0]。次にa[$0]に設定され1+xます。
ジル「SO-悪であるのをやめる」14年

7
awkの動作に関する分析は正しいと思います。昨日そうでないと暗示してすみません。しかし、Archemarの答えに対するあなたの批判は間違っています。Archemarは優先順位を誤解していません。優先順位を評価の順序と混同しているのです(前のコメントを参照)。アーキマールの回答に関する記述を削除した場合、回答は正しいはずです。現状では、Archemarが間違っていることを証明することに焦点を当てていますが、そうではありません。
ジル「SO-悪であるのをやめる」14年

5
まあ、少なくとも今私は... AWKのデバッガを知っている
Archemar
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.