はい、私はITを知っています。それはFOSSです。 今週のチャレンジでは、端末画面を表示します。そして、私たちが望んだ結果を得るのを手助けするためにあなたを頼りにします。 多くの解決策があり得ます、そして創造的であることは挑戦の最も面白い部分です。
まだ行っていない場合は、以前の課題を見てください。
- バッシュチャレンジ6
- バッシュチャレンジ5
あなたは本の形でこれらの課題を(未発表の課題で)購入し、私たちをサポートすることもできます。
プレイする準備はできましたか? だから今週の挑戦はここにあります。
トークンカウンター
今週は、より「プログラミング指向」の課題に戻ります。 説明は少し抽象的なので、数分間私と一緒にいるようにしてください - そして私は以下の説明が十分に明確になることを願っています:

私は「RED」または「BLUE」のトークンのストリームを持っています。 必要に応じて、例えばイベントストリームの表現としてそれを考えることができます。 私はそのストリームを特に制御することはできません。 予想外に、どちらか一方のトークンが生成されることを私は知っています。 そして私は、蒸気は有限であることを知っています(すなわち、ある時点で、それ以上読むべきデータがなくなるでしょう)。
この課題のために、私はそのストリームを生成するためにBash関数を使用しました。 あなたはとにかくそれを変更することはできません。
# You MUST NOT change that : stream() { TOKENS=( "RED" "BLUE" ) for((i=0;i<100;++i)) ; do echo ${TOKENS[RANDOM%2]} done }
私の目標は彼の流れの中にあったRED と BLUEトークンの両方の数を数えることです。 私自身では、REDトークンの数だけを数えるための解決策を見つけることができました。
# You MUST change that stream | \ grep -F RED | wc -l > RED.CNT cat RED.CNT
残念ながら、REDトークンと BLUEトークンの両方を数えるための解決策は見つかりませんでした。 だからこそ私はあなたの助けが必要です。 何か案が ?
以下のコメント欄であなたの解決策を読むのを楽しみにしています!
詳細が少ない
この課題を解決するために、私は使用しました:
GNU Bash、バージョン4.4.5(x86_64-pc-linux-gnu)
- Debian 4.8.7-1(amd64)
- すべてのコマンドは、標準のDebianディストリビューションに同梱されているものです。
エイリアスが設定されたコマンドはありません
ソリューション
再現方法
これが、この課題を解決するために使用した生のコードです。 これを端末で実行すると、チャレンジの図に表示されているのとまったく同じ結果を再現できます(私と同じソフトウェアバージョンを使用していると仮定します)。
rm -rf ItsFOSS mkdir -p ItsFOSS cd ItsFOSS clear stream() { TOKENS=( "RED" "BLUE" ) for((i=0;i RED.CNT cat RED.CNT
なにが問題だったの ?
ここでの唯一の問題は、データストリームを直接 grep
送信するため、最初の試行で入力の一部を破棄することでした。
基本的にその問題を解決するための3つのアプローチがあります:
ストリームデータを保存し、後でそれらを処理します。
- ストリームを複製し、REDトークンとBLUEトークン用に2つの独立したパスを処理します。
- 両方のケースを同じコマンドで処理してください。
その価値のために、それぞれの解決策の後に、私は私のシステムで観察されたリアルタイムの使用法を示します。 これはあくまで目安であり、慎重に行う必要があります。 だから、自分で比較してみてください。
店舗とプロセスのアプローチ
ストアアンドプロセスアプローチの最も簡単な実装は明らかです。
stream > stream.cache grep -F RED RED.CNT grep -F BLUE BLUE.CNT rm stream.cache (1.3s for 10, 000, 000 tokens)
機能しますが、いくつかの欠点があります。データを保存する必要があり、データはトークンごとに順番に処理されるということです。 もっと微妙なことですが、 stream.cache
ファイルを2回読むと、並行プロセスが処理中にそのファイルを更新すると、競合状態になる可能性があります。
それでもストアアンドプロセスのカテゴリには、まったく異なる解決策があります。
stream | sort | uniq -c (5.9s for 10, 000, 000 tokens)
sort
コマンドはそれらを処理することができる前にすべてのデータを (RAMまたはディスクのどちらかに)最初に読み込みそして保存しなければならないので、私はストアアンドプロセスアプローチを考慮します。 より正確には、私のDebianシステムでは、 sortコマンドは/tmp
にrw権限を持ついくつかの一時ファイルを作成します。 基本的に、この解決方法は最初のものと同じ欠点を持っていますが、最も悪い性能を持っています。
複製ストリーム
それらを処理する前に/データを/保存する必要がありますか? いいえ。もっと賢い考えは、ストリームを2つの部分に分割し、各サブストリームで1種類のトークンを処理することです。
stream | tee >(grep -F RED | wc -l > RED.CNT) \ >(grep -F BLUE | wc -l > BLUE.CNT) \ > /dev/null (0.8s for 10, 000, 000)
ここには中間ファイルはありません。 tee
コマンドは、ストリームデータを到着時に複製します。 各処理ユニットはそれ自身のデータのコピーを得て、それらをその場で処理することができる。
これは、データが到着したときにデータを処理するだけでなく、 並列処理を行うようになったため、賢い考えです。
到着時にデータを処理する
コンピュータサイエンスでは、おそらく以前の解決策が問題に対して機能的なアプローチを取ったと言うでしょう。 一方、次の解決策は純粋に必須の解決策になります。 ここでは、それぞれのトークンを読み、/これがREDトークンの場合、/ then / REDカウンターをインクリメントします。それ以外の場合/これがBLUEトークンの場合、BLUEカウンターをインクリメントします。
これは、そのアイデアの単純なBash実装です。
declare -i RED=0 BLUE=0 stream | while read TOKEN; do case "$TOKEN" in RED) RED+=1 ;; BLUE) BLUE+=1 ;; esac done (103.2s for 10, 000, 000 tokens)
最後に、 AWK
コマンドの大ファンである私は、それをきちんとしたエレガントな方法で解決するためにそれを使用するという誘惑には抵抗しません。
stream | awk ' /RED/ { RED++ } /BLUE/ { BLUE++ } END { printf "%5d %5d\n", RED, BLUE } ' (2.6s for 10, 000, 000 tokens)
私のAWKプログラムは3つの規則で構成されています。
REDという単語を含む行に遭遇したとき、REDカウンターを増やします(
++
)- 単語BLUEを含む行に遭遇したとき、BLUEカウンターを増やします
入力の終わりに、両方のカウンターを表示します。
もちろん、数学演算子のために、 初期化されていない AWK
変数はゼロであると仮定されることをあなたが知っていなければならないことを完全に理解するために。
それは素晴らしい仕事です。 ただし、トークンごとに同じ規則を複製する必要があります。 ここには大したことはありません。2つの異なるトークンしかありません。 私たちがそれらをたくさん持っているともっと厄介です。 それを解決するために、 配列に頼ることができます :
stream | awk ' { C[$0]++ } END { printf "%5d %5d\n", C["RED"], C["BLUE"] } ' (2.0s for 10, 000, 000 tokens)
ここで必要なのは、トークンの数に関係なく、2つの規則だけです。
読み取りトークン(
$0
)が何であれ、対応する配列セルを大きくします(ここでは、C["RED"]
またはC["BLUE"]
いずれか)。入力の終わりに、
"RED"
"BLUE"
セルと"BLUE"
セルの両方の配列の内容を表示します。
"RED"
と"BLUE"
が文字列になっていることに注意してください(それらの周りに二重引用符がありますか?)そしてAWK
は連想配列をサポートしているのでこれは問題になりません。 また、プレーン変数と同じように、 AWK
連想配列の初期化されていないセルは、数学演算子ではゼロと見なされます。
前に説明したように、ここではAWK
を使用することを選択しました。 しかし、 Perl
ファンはこの問題について異なる意見を持っているかもしれません。 あなたがそれらのうちの1人なら、なぜあなた自身の解決策をコメント欄に投稿しませんか?
とにかく、私たちはあなたがその挑戦を楽しんだことを望みます。 そしてもっと楽しみにしていてください!