第63回シェル芸勉強会
参加しました。今回は正規表現とか検索の回とのことでした。参加記として解答とPowerShell解を残しておきます。
当日の配信はこちらです。
問題1
九九の合計を出力する問題。えいでできそうですね。
$ echo {1..9}'*'{1..9} | fmt -1 | bc | jq -s add 2025 # 別解 $ seq 9 | join -j9 -t\* - <(seq 9) -o 1.1,2.1 | paste -sd+ | bc
組み合わせを生成して合計するだけですね。PowerShell解も考えてみます
$ (1..9 | %{ $x=$_; 1..9 | %{ $_ * $x } } | measure -Sum).Sum
普通に計算した感じですね。
問題2
9132円の支払いとして、10000円を出したとき、お釣りとしてあり得る効果の組み合わせを出力する問題。組み合わせは1種類でいいとのことなので、1円玉868枚と出力するだけでいいのですが、それだとつまらないのでできるだけ出力してみましょう。
$ join -j9 <(echo 500'*'{0,1} | fmt -1) <(echo 100'*'{0..8} | fmt -1) | join -j9 - <(echo 50'*'{0..17}|fmt -1) | sel -a -D+ 2: | sel 1 1 | teip -f2 -- bc | awk '$2<=868{for(ten=0;ten<=int((868-$2)/10);ten++) for(one=0;one<=int(868-ten*10-$2);one++)if($2+ten*10+one == 868) print $1"+10*"ten"+1*"one}' ... 500*1+100*2+50*2+10*1+1*58 500*1+100*2+50*2+10*2+1*48 500*1+100*2+50*2+10*3+1*38 500*1+100*2+50*2+10*4+1*28 500*1+100*2+50*2+10*5+1*18 500*1+100*2+50*2+10*6+1*8 500*1+100*2+50*3+10*0+1*18 500*1+100*2+50*3+10*1+1*8 500*1+100*3+50*0+10*0+1*68 500*1+100*3+50*0+10*1+1*58 500*1+100*3+50*0+10*2+1*48 500*1+100*3+50*0+10*3+1*38 500*1+100*3+50*0+10*4+1*28 500*1+100*3+50*0+10*5+1*18 500*1+100*3+50*0+10*6+1*8 500*1+100*3+50*1+10*0+1*18 500*1+100*3+50*1+10*1+1*8
500円玉、100円玉、50円玉の組み合わせを列挙、一度この時点で868円を超える組み合わせは除外します。最初から組み合わせを列挙すると、組み合わせの量が多すぎてえらいことになるのでこうしてます。
10円玉と1円玉の部分はawk
で組み合わせ列挙、868円となるものだけ出力してます。
同じ方針でのPowerShell解は以下です。
$ 0..1 | %{ @{ str="$_*500"; sum=$_*500 } } | %{for($i=0;$i -le 8;$i++){@{sum=$_.sum+$i*100;str=$_.str+"+$i*100"}}} | %{for($i=0;$i -le 17;$i++){@{sum=$_.sum+$i*50;str=$_.str+"+$i*50"}}} | ?{$_.sum -le 868} | %{for($i=0;$i -le 86;$i++){@{sum=$_.sum+$i*10;str=$_.str+"+$i*10"}}} | ?{$_.sum -le 868} | %{$d=868-$_.sum;$_.str+"+$d*1"}
問題3
genkou.tex
からペアになっていない\ref
、\label
を探す問題。160本ノックに類題がありました。という話もされましたね。
$ cat She*/*.63/gen* | grep -oPe '\\(label|ref)\{[^}]+}' | sel -gd '\\|\{|\}' 3 | sort | uniq -u eq:state_equation_linear eq:state_equation_nonlinear fig:typhoon
欲しいデータだけ削っていき、uniq -u
で重複部分だけ取り出すって感じですね。uniq
のオプションは覚えられんです。サッと出てくる人はすごいです
PowerShell解は以下です。
$ Get-Content -Encoding UTF-8 She*/*.63/gen* | sls '\\(ref|label)\{[^{]+}' | %{ $_.Matches.Value } | sort -Unique | %{ $x=($_ -split '{'); @{a=$x[0];b=$x[1]} } | group -Property b | ?{$_.Group.Count -eq 2 }|%{$_.Name} eq:state_transition_model} eq:state_transition_model2} fig:motion}
問題4
tonnan.txt
から東西南北
となっている部分文字列を抽出する問題。ただし、東西南北
の並び順は考えなくて良いとのこと。
$ cat She*/*.63/to* | conv fs='' 4 | zniq fs='' | awk 'length($1)==4{print NR"文字目から"$1}' 3文字目から西南北東 12文字目から南北西東 33文字目から南東西北 41文字目から西北南東 43文字目から南東北西 54文字目から東北南西 55文字目から北南西東 56文字目から南西東北 57文字目から西東北南 58文字目から東北南西 63文字目から南西東北 64文字目から西東北南 68文字目から東南西北 71文字目から北西南東 72文字目から西南東北 97文字目から西北東南
egzact
の出番です!
先頭からずらしながら4つずつ選んでいくのはconv
を使います。fs=''
とすることで一文字ずつをカラムにできるので楽です。
東西南北が揃っているかどうかは、横方向のuniq
をした後の文字列が4文字かどうかで判定できます。横方向のuniq
にはzniq
を使います。これもegzact
のコマンドです。便利~~~!
PowerShell解もやってみます。こちらはegzact
無しです
$ $TEXT=(Get-Content -Encoding utf8 Sh*/*.63/to*); 0..$TEXT.Length | %{ $x=$TEXT[$_..($_+3)]; if(($x | sort -Uniq).Length -eq 4) { "$_ "+($x -join '') } }
$TEXT
に文字列を取り出しておいて、スライスで切り出していきます。残りの部分はzniq
や、awk
でやってることと同じですね
問題5
reversi.txt
は以下のような内容のファイルです
12345678 A B C ⚪ D ⚪⚫ E ⚫⚫ F ⚪⚫ G H
次の手番は⚪
です。いくつかおける場所は何個かありますが、斜めにひっくりかえせる場所である、E6, G7に❌
をおいてくださいという問題。ただし、普通にやるだけだと面白くないので、同じsed
二回だけで完成させてくださいとのこと。どうして。
cat reversi.txt | sed '式' | sed '式'
難しそうですが、ひっくりかえせるかどうかを調べるべき座標は、⚪
から固定数倍個先であることが分かれば正規表現を書くのはやるだけです。
$ cat She*/*.63/re* | sed -zE 's/(⚪.{10}(⚫.{10})+) /\1❌/' | sed -zE 's/(⚪.{10}(⚫.{10})+) /\1❌/' 12345678 A B C ⚪ D ⚪⚫ E ⚫⚫❌ F ⚪⚫ G ❌ H
はい…。これがやるだけな正規表現かどうかは謎なんですが…。ここからの話はsed -zE
を前提にします。
盤面をななめ見るとき、駒と駒の間には改行を含めて10文字が存在します。最初は⚪
の次に⚫
が来なければなりません。これを正規表現で表すと⚪.{10}⚫
と書けます。
その先の駒も考えてみます。❌
の位置に⚪
を置くことで斜めにひっくりかえせることを考えると、⚫
の次は⚫
でなければなりません。正規表現で書くと⚫.{10}⚫
となりますが、⚫
は何個続いてもいいので、+
を使った繰り返しを用いて(⚫.{10})+
と表すことができます。
⚪
から⚫
の正規表現と⚫
から⚫
の正規表現を連結すると、⚪.{10}(⚫.{10})+
と表せます。最後、空白マスである
が登場したら、そこが❌
を置きたい場所になるので置換を行います。このとき、⚪
~⚫
~⚫
~の部分はそのまま使いたいので、キャプチャして後方参照します。
なんでこれが二回なのかというと、G7に置くときの始点であるD4は、E6の検索中にすでに通過している部分であり、sed
ではいったん戻って再検索ということができないからですね。
なんというか、信じられないほど読みづらい文章ですね。むずかしいね。
PowerShell解は…勘弁してください。
問題6
先ほど❌
を置いた位置と⚪
の間にある⚫
を⚪
にする問題。やっぱりねー!予想ついてましたよ!
この問題ではPerl
を使うと楽とのこと。先読みやら後読みやらが使えるからですね!最強!
$ cat She*/*.63/re* | sed -zE 's/(⚪.{10}(⚫.{10})+) /\1❌/' | sed -zE 's/(⚪.{10}(⚫.{10})+) /\1❌/' | tr -d \\n | perl -C -Mutf8 -pe 's/⚫(?=.{9}(⚫.{9})*❌)/⚪/g' | fold -b27 | awk 4 12345678 A B C ⚪ D ⚪⚪ E ⚫⚪❌ F ⚪⚪ G ❌ H
時間切れでした。正規表現を書くところまではよかったんですが、-Mutf8
にハマってしまいました。-C
が必要ってところまでは過去の午前の部で習ったので覚えていたんですが…。無念。
LT
今回もLTさせていただきました!聞いてくださった方ありがとうございました!
音声合成やってみたいな…と思ったのでやってみたというお話です。筋トレも頑張ります。
まとめ
今回は2問目をサッと解けるような回答が出せなくて悔しかったですね。最後2問も難しそうなんですが、パターンに気づけるかどうかだったので頭の体操って感じでいいなって思いました。楽しんで解くことができました!
途中、もなかを頂いたんですが、これが糖分補給にめちゃめちゃ良かったです。次回から必須アイテムかもですね(?)
今回も企画・開催・準備などありがとうございました!