去年のcode festivalの際にshinhさんにgolfの練習法を聞いたらanagolの過去問を勧められたので、それをやっていこうと思う。

perlで考えていて300byteぐらいのしか書ける気がしなかったので諦めて他人のを読みました。bashやってたら可能だったかもしれないが、perlでこの展開は思い付けない。rubyのそれはglob使ってないのに短くてすごい。

easy regexp

whio氏 81byte perl

s!.(\w+)]!(@{[$1=~/./g]})!,y/()| /{},/,s/(B|{[^}]+})\?/{,$1}/g,print<"$_\n">for<>

globを上手く使っている。 手元では最後の\nを消さないとちゃんと動かなかった。

平たく整形するとこう。

$_=<>;
s!.(\w+)]!(@{[$1=~/./g]})!;
y/()| /{},/;
s/(B|{[^}]+})\?/{,$1}/g;
print < "$_\n" >;

以下のように変形し、

  1. (Objective-|Aspect|Concept)?[CD](++|--|#|omega)?
  2. (Objective-|Aspect|Concept)?(C D)(++|--|#|omega)?
  3. {Objective-,Aspect,Concept}?{C,D}{++,--,#,omega}?
  4. {,{Objective-,Aspect,Concept}}{C,D}{,{++,--,#,omega}}

最後に<file*glob>を使って展開している。

まずs!.(\w+)]!(@{[$1=~/./g]})!;[]の処理。[(\w+)]とは書けないので.(\w+)]と上手く回避し、$1=~/./gつまりsplit//,$1として文字に分割し、listにして文字列に埋め込むことで間に空白を挟んでいる。

y/()| /{},/;は以下のような挙動を使っている。

$ echo abcdef | perl -pe 'y/abcde/ABC/'
ABCCCC

s/(B|{[^}]+})\?/{,$1}/g;?の処理。テストケースを見て必要なものだけ処理している。

最後の展開におけるglobとはつまり

$ bash
$ echo {a,b,c}
a b c

ってなるアレ。他にも

$ ls
a.pl  a.pl~  tags.lock  test
$ perl -e 'print <"*.pl">'
a.pl

とかできる。

flagitious氏 114byte ruby

def r n,s=''
n[/(\W(.*?)[])]|.)\??/]?(l=$'
r l,s if$&['?']
$+.split($+['|']||'').map{|j|r l,s+j}):puts(s)end
r *$<

普通に再帰降下してる。 1.8なrubyでの提出だけど手元の2.3でも動いた。

整形。

def r(n,s='')
    if n[/(\W(.*?)[])]|.)\??/]
        l=$'
        r l,s if $&['?']
        $+.split($+['|']||'').map{|j|r l,s+j}
    else
        puts(s)
    end
end
r *$<

rubyの関数定義defの括弧は省略できる。

まずr *$<r(*ARGF)であり、この場合r getsと同じ。

if n[/(\W(.*?)[])]|.)\??/]String#[]。ここにおいてはString#=~で完全に置き換えられる。?まで含むひとつの塊の切り出し。

l=$'はマッチより後ろの部分を保存。

r l,s if $&['?']?により無視された場合の処理。$&

$+.split($+['|']||'').map{|j|r l,s+j}が主な再帰。 $+

現在のスコープで最後に成功した正規表現のパターンマッチでマッチした中で最後の括弧に対応する部分文字列

であり、{} ()の内側あるいは単一の文字。これを適当に| で切り分け、そのそれぞれに関して再帰している。