web-dev-qa-db-ja.com

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

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

例えば:

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

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

awk '!_[$0]++'

_は、Perlのようにawkで特別な意味を持ちますが、配列の名前にすぎません。

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

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

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

42

どれどれ、

 !a[$0]++

最初

 a[$0]

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

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

 !a[$0]

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

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

いいですね、見つけて!あなたはコードゴルフを見ておくべきです!

37
Archemar

ここに処理があります:

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

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

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

参照:

gawkを使用すると、 dgawk(またはawk --debug with new version) を使用してgawkスクリプトをデバッグできます。まず、test.awkという名前のgawkスクリプトを作成します。

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_postincrementOp_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++;
30
cuonglm