はのちゃ爆発

はのちゃが技術ネタとか日常のこととかを書いてます。

おまけ: builderscon で配られたトートバッグに書かれているプログラムを読んでみる

ネタばれみたいな感じになっちゃうのかな、マズかったら教えてください。

builderscon tokyo 2016 のノベルティとして配られていたトートバッグに、あるプログラムが書かれていました。

[1]pry(main)> '6A6F622D64726166742E6A702070726573656E746564206279206C69766573656E7365'.scan(/.{1,2}/).map(&:hex).map(&:chr).join

何の言語か…は言うに及ばず、ご丁寧に頭に '[1]pry(main)' って書いてある通り、Rubyですね。

いきなりRubyで実行してもいいんですが、それじゃつまらないので、実行する前にちゃんとどうなるか予測してからにしましょう。

scan(/.{1,2}/)

はじめの謎英数字の文字列…が何かは考えてもしょうがないのでいったん置いておくとして。

文字列に対してまず scan(/.{1,2}/) してます。 scan の中身は正規表現ですね。 String#scan は、レシーバの文字列に対して引数で与えられたパターンを繰り返しマッチし、マッチした文字列の配列を返してくれます。

ちょっと難しそうに見えますが、ここでやってることは「与えられた文字列を2文字ずつに区切って配列にする」というだけの操作です。 得られる配列は以下のようになります。

=> ["6A", "6F", "62", "2D", "64", "72", "61", "66", "74", "2E", "6A", "70", "20", "70", "72", "65", "73", "65", "6E", "74", "65", "64", "20", "62", "79", "20", "6C", "69", "76", "65", "73", "65", "6E", "73", "65"]

map(&:hex)

さて、次は map(&:hex) です。なんとなく挙動は読める気もしますが、ちゃんと調べましょう。

map は、配列の各要素に対してブロックを評価し、その結果を格納した配列を返すものです。

…あれ?渡されてるものが一見ブロックじゃないですね? これは下のページにちょろっと書いてあるやつですね。

メソッド呼び出し(super・ブロック付き・yield) (Ruby 2.3.0)

to_proc メソッドを持つオブジェクトならば、`&' 修飾した引数として渡すことができます。

:hex はシンボルなので to_proc メソッドを持っています。そのため、この書き方はブロック付きメソッド呼び出しをしたのと等価になります。 もっと詳しい解説は以下のQiita記事が参考になると思います。(弊社先輩社員の記事びっくりしました)

qiita.com

なお、 String#hex メソッドは、レシーバの文字列を16進数と解釈して、対応する10進数の整数値に変換するメソッドです。

ここまでの処理をまとめて日本語で言うと、

「2文字ごとに区切られた文字列を16進数と解釈して、それぞれを10進数に変換した配列にする」

という感じになります。

map(&:chr)

上記の map(&:hex) と似てるので細かい解説は省略します。 :chr なのでたぶんなんとなく分かると思いますが、そのまんまです。

「10進数になった数値を文字コードと解釈して、対応する文字(1文字からなる文字列)に変換する」という処理です。

ちなみにエンコーディングの指定がないのですが、この場合、

引数無しで呼ばれた場合は self を US-ASCII、ASCII-8BIT、デフォルト内部エンコーディングの順で優先的に解釈します。

となるそうです。

join

わざわざ節作るほどのことではない

配列をすべてつなげて最終的な文字列を得ます。

結果は…

結果は皆さん実行してからのお楽しみ、ということで。