熱血!平成教育学院の今夜の放送の問題より。復号(暗号化も同じ)のためのスクリプトを Squeak の Smalltalk を使ったいくつかの方法で。
■対応する復号文字の ASCII コードを計算で求めて #collect:
| codeString | codeString := 'HZPFIZ'. ^ codeString collect: [:chr | | asciiValue | asciiValue := $Z asciiValue - chr asciiValue + $A asciiValue. Character value: asciiValue]
=> 'SAKURA'
■あらかじめ作成したテーブルから復号文字を探して #collect:
| codeString tableDict alphabet | codeString := 'HZPFIZ'. tableDict := Dictionary new. alphabet := Character alphabet asUppercase. alphabet with: alphabet reversed do: [:key :val | tableDict at: key put: val]. ^ codeString collect: [:chr | tableDict at: chr]
■ #translateWith: を用いて一括変換
| codeString tableArray alphabet start | codeString := 'HZPFIZ'. tableArray := Character allByteCharacters. alphabet := Character alphabet asUppercase. tableArray replaceFrom: (start := tableArray indexOf: $A) to: start+25 with: alphabet reversed. ^ codeString translateWith: tableArray
■ #translateWith: 版で、用いるテーブルの情報を必要最小限にして
| codeString tableArray | codeString := 'HZPFIZ'. tableArray := (Array new: $A asciiValue), Character alphabet asUppercase reversed. ^ codeString translateWith: tableArray
ここまでシンプルにできると、ワンライナで書いてもさほどの違和感はない。
'HZPFIZ' translateWith: (Array new: $A asciiValue), ($A to: $Z) reversed
=> 'SAKURA'
さらに短く。
'MVPPVGHF' translateWith: (Array new: 65), ($A to: $Z) reversed
=> 'NEKKETSU'
これで最短?
'HJFVZP' translateWith: (1 to: 65), ($A to: $Z) reversed " => 'SQUEAK' "
こういうのは Perl and/or 正規表現が得意そう…なのですが、残念ながら私のほぼ無に等しい Perl 力ではワンライナで書くことはできませんでした。
コメント欄で nurse さんが Perl の tr/// 、s/// を使った例を教えてくださいました。ありがとうございます。
echo HZPFIZ | perl -pe 'tr/A-Z/ZYXWVUTSRQPONMLKJIHGFEDCBA/'
echo HZPFIZ | perl -pe 's/[A-Z]/chr 155-ord$&/ge'
前者については、Smalltalk の #reversed や Ruby の reverse のようなものが Perl にもあれば、もう一段、抽象度が上げられそうです。(…とは言っても、変数を使ったり、eval しなくてはいけなくなるので、一歩進んで二歩下がるwって感じでしょうか。って、ありますね。reverse 。Perl にも。当然。w)
さらに tociyuki さんが、このエントリーを受けて、Perl や Ruby でいろいろな方法で書いてくださっています。ありがとうございます。
私は Ruby で書いた次のものが理想像としてイメージしているのに近かったです。Range にもうちょっと融通が利いて、to_a しなくてよければよりベターなのですが…。
'HZPFIZ'.tr!("A-Z", ("A".."Z").to_a.reverse.join)
Perl のほうは、これがイメージに近いです。前述のとおり、eval が邪魔くさいですが…(^_^;)。
echo HZPFIZ | perl -pe 'eval qq{tr/A-Z/@{reverse A..Z}/};'
と書いたら、新しいコメントで、shinichiro_h さんに Ruby の [*"A".."Z"] という表現を教えていただきました。すばらしい。ただ、たしかに、表記上の冗長さが減る代わりに、新たな表現方法の解釈をしないといけなくなるので、総じて分かりやすさの面では一進一退…かもしれませんね。
'HZPFIZ'.tr!("A-Z", [*"A".."Z"].reverse!.join)
kounoike さんから、Golfed 'SAKURA' => 'HZPFIZ' | Typemiss.net にて、Perl ゴルファーの心得に添えて Perl の別解などをいただきました。ありがとうございます。(言われて気づいたのですが、上の Squeak の Smalltalk の例は、ルール1を前提にしちゃっていますから Perl や Ruby の例と同列に扱ってはいけなかったですね…(^_^;)。まあ、ハンデだと思ってご勘弁を。)
で、いただいた別解はこちら。
#!perl print+(A..CL)[-ord]for<>=~/./g
一瞬、なぜこれでうまく動くのか分かりませんでした。w Squeak の Smalltalk で書くと、このような内容でしょうか。
'HZPFIZ' collect: [:chr | ($A to: (Character value: $A asciiValue + $Z asciiValue)) atWrap: chr asciiValue negated]