読者です 読者をやめる 読者になる 読者になる

カイワレの大冒険 Third

技術的なことや他愛もないことをたまに書いてます

concatメソッドにハマった件

プログラミング プログラミング-Ruby

またもやRubyネタですが、とあるリストの各要素同じ文字列追記をしたかったのですが、そしたら見事ハマったので書いておきます。

やりたいことはこんな感じ。

  • とあるリストがある。要素数は3にしておきます。
  • その各要素に対して、文字列を末尾に加えたい。
  • mapで回して、concatすればいいんじゃないかな
  • どうしてこうなった

という感じなので、説明していきます。にわかでRuby書いてる人間なので、間違い等あったら、遠慮無くご指摘お願いします。

まず、リスト。

>>> list_a = ['value1', 'value2', 'value3']
>>> puts list_a
value1
value2
value3

ここまでは特に問題ありません。 んで、ループさせてみる。

>>> list_a = ['value1', 'value2', 'value3']
>>> puts list_a.each{ |val| val }
value1
value2
value3

これも問題ない。

んで、value1, value2, value3という文字列に「desu」という文字列をくっつけてみましょう。

>>> list_a = ['value1', 'value2', 'value3']
>>> puts list_a.map{ |val| val.concat("desu") }
value1desu
value2desu
value3desu

期待どおりでございます。concatというのは引数に取った値をレシーバに結合するメソッドで文字列を返してくれます。 ドキュメントはこちら。 mapを使ってるのは新たなリストを返したかったからですね。

今回僕が使いたかった場面としては、

list_dir = ['/work1', '/work2', '/work3']

みたいなディレクトリのリストがあって、その最後に「/test」みたくサブディレクトリをリスト化したかったわけです。 んで、↑のようにやってみたと。

そしたら、こんな問題に遭遇したわけです。

>>> list_a = ['value1', 'value2', 'value3']
>>> list_a_desu = list_a.map{ |val| val.concat("desu") }
>>> puts list_a_desu
>>> list_b_deshita = list_a.map{ |val| val.concat("deshita") }
>>> puts list_b_deshita
value1desu
value2desu
value3desu
value1desudeshita
value2desudeshita
value3desudeshita

list_b_deshitaに期待してるのは、valu1deshita, valu2deshita, valu3deshitaなんですが、なぜかうまくいかん。 mapが悪いじゃないかということで、eachにしたり悩んで、同僚に聞いたところ、concatじゃないかと。

concatって破壊的メソッドなんですね。ということで、2行目でconcatメソッド使った僕はlist_aのオブジェクトを容赦なく変えていました。 「<<」を使っても同じですな。

ということで、「+」にしてみる。

>>> list_a = ['value1', 'value2', 'value3']
>>> list_a_desu = list_a.map{ |val| val + "desu" }
>>> puts list_a_desu
>>> list_b_deshita = list_a.map{ |val| val + "deshita" }
>>> puts list_b_deshita
value1desu
value2desu
value3desu
value1deshita
value2deshita
value3deshita

ということで、無事でけた。ということで、破壊的メソッドはなめたらあかんということを身をもって知った日でした。

おまけ

「+」というのはメソッドでして、ドキュメントにこんなことが書かれています。

演算子式において、「再定義可能な演算子」に分類された演算子の実装 はメソッドなので、定義することが可能です。

ということで、メソッドでございます。

たとえば、こんな書き方ができる。

list_a = ['value1', 'value2', 'value3']
puts list_a.map{ |val| val.+("desu") }

メソッドなので、チェーンメソッドにし、さらにカッコつけて引数を渡すことができる。 今まで、「+」ってなんかメソッドっぽくなくて、concatとか「<<」のほうがスマートじゃないかと思ってましたが、 僕が浅はかでした。立派なメソッドでした…

この素敵なサイトにあるように、

そう、Rubyの世界で + は演算子ではなく、ユーザが再定義可能なひとつのメソッドに過ぎないのだ。 疑い深いあなたはこれだけでは納得しないかも知れない。そしてきっと、他の演算子についても試してみるのだろう。

Rubyは恐ろしいw Pythonも演算子のオーバーロードができるみたいですね(http://atkonn.blogspot.jp/2008/02/python-python50.html)。 言語の世界は深い…