たいちょーの雑記

ぼくが3日に一度くらい雑記をかくところ

第63回シェル芸勉強会に参加しました

第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の出番です!

github.com

先頭からずらしながら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させていただきました!聞いてくださった方ありがとうございました!

speakerdeck.com

音声合成やってみたいな…と思ったのでやってみたというお話です。筋トレも頑張ります。

まとめ

今回は2問目をサッと解けるような回答が出せなくて悔しかったですね。最後2問も難しそうなんですが、パターンに気づけるかどうかだったので頭の体操って感じでいいなって思いました。楽しんで解くことができました!

途中、もなかを頂いたんですが、これが糖分補給にめちゃめちゃ良かったです。次回から必須アイテムかもですね(?)

今回も企画・開催・準備などありがとうございました!