Edited at

Rubyで文字列を `Kenkenization` する

うちのチームにkenkenというニックネームのメンバーがいて、日々がんばっています。

今日、pyamaさんというひとが「kenkenってタイプするの面倒だからkenにしなよ」と横暴なことをいいだして、「こりゃマズい、kenkenの入力を効率化しないと勝手に名前を変えられてしまう!」というわけで、みんなで知恵をふりしぼりました。


既存手法

kenkenという文字列は、kenという文字列がふたつ並んでいます。つまり、プログラマの三大美徳のひとつ「怠惰」という観点からすると、同じ文字列の入力を2回繰り返すのは、とてもダルいことです。

これを解決するために、まずはストレートに以下の案がでました。

2.times { print "ken" }

確かに、2回同じ文字列の入力を繰り返すことは避けられました、しかし、繰り返しは避けられたものの、puts "kenken"に比べて、総入力文字数という観点では、むしろダルくなっているのは否めません。

そこで、以下のようなやり方が提案されました。

puts "ken" * 2

確かに、これならそこそこ短い入力で、kenkenという文字列を出力できます。しかし、当たり前過ぎて、特に面白い回答とはいえません。


提案手法

わたくしたちは考えました。以下のような書き方で文字列kenkenを出力できるとしたら、かっこいいのではないかと。

puts ken ^ 2 #=> "kenken"

そこで作成したのが https://github.com/kentaro/kenken です。このライブラリを使うと、文字列kenkenの出力を以下のように書けます。


Kenkenクラスを使う場合

ken = Kenken.new("ken")

puts ken ^ 2 #=> "kenken"


Stringクラス拡張を使う場合

ken = "ken"

puts ken ^ 2 #=> "kenken"

これだけだと、「まあそうだよね」的な感じですよね。しかし、上記はあえてミスリーディングな例を示しています。このモジュールの真の力はここから発揮されます。以下の例をご覧ください。


Kenkenクラスを使う場合

ken = Kenken.new("hayapi")

puts ken ^ 2 #=> "kenken"


Stringクラス拡張を使う場合

ken = "hayapi"

puts ken ^ 2 #=> "kenken"

おわかりでしょうか。つまり、引数にわたされた文字列によってkenkenという文字列を出力しているのではなく、変数名によって文字列を出力していることが。ここまでやれば、pyamaさんといえども、kenkenというニックネームを認めざるを得ないでしょう。


技術的な解説

Kenkenクラス、および、拡張されているStringクラスともに、上記の処理を実装したKenkenizerモジュールをmixinしています。Kenkenizerモジュールの実装は以下の通りです。

module Kenkenizer

def ^(count = 1)
b = binding.of_caller(1)
var = b.eval('local_variables').find do |v|
var = b.eval(v.to_s)
self == var
end

var.to_s * count
end
end

ポイントは、binding.of_caller(1)という箇所です。これはbinding_of_callerというgemの提供するメソッドです。呼び出し元をさかのぼって、レベルごとのbinding、すなわち環境をいい感じにとってこれるというものです。

binding.of_caller(1)でひとつ上の環境をとってきたあとにb.eval('local_variables')とすることで、その環境での変数名(Symbol)の一覧を取り出し、さらにそれらをその環境で評価することでオブジェクトにします。それらのオブジェクトと自分自身を比較して、一致するものを自分自身を表す変数として判定します。

これで、無事に変数名から動的に、Kenkenizeするべき文字列を取り出せました。すなわち、Kenkenizeとは、


  1. 変数名から出力に使う文字列を動的に取り出す


  2. ^メソッドにより、その文字列を繰り返す

という処理のことをいいます。それを実現するためにわたくしたちが開発したのが https://github.com/kentaro/kenken ということになります。


おわりに

kenkenと、kenを繰り返し入力するのはダルいというのは、プログラマの三大美徳的の「怠惰」的に、自然な感情であるとはいえます。しかし、ひとのニックネームをプログラマの都合で変えるのは誤りです。かといって、ただその誤りを精神論的に責めてもしかたありません。プログラマなら技術で対抗するべきでしょう。そのため、わたくしたちはこのようなライブラリを実装したのでした。

この記事が、みなさまの参考になれば幸いです。