Railsチュートリアル(第7版)第4章4.3.2、「ブロック」の演習過程で生まれた疑問を解消したので記録する。(Rails 7.0.4.3)
筆者の習熟度:1週間前にプログラミングの勉強を開始した。全ての基礎をすっ飛ばしてRailsチュートリアルを進めている。現在4章を進行中。
事の発端となった問題文がこちら。
yeller(大声で叫ぶ)というメソッドを定義してください。このメソッドは、文字列の要素で構成された配列を受け取り、各要素を連結した後、大文字にして結果を返します。例えばyeller(['o', 'l', 'd'])と実行したとき、"OLD"という結果が返ってくれば成功です。(ヒント:mapとupcaseとjoinメソッドを使ってみましょう。)
引用:Ruby on Rails チュートリアル(第7版)第4章
この問題の答え1は以下のとおり。
>> def yeller(s)
>> s.map(&:upcase).join
>> end
# yeller(["o", "l", "d"]) を実行すると "OLD" が出力される
チュートリアルのおかげでどういう処理をしているかは何となく理解できたが、同時に疑問が生まれてきた。
mapとjoinは逆じゃダメなん?
結論。逆でもいい。
そもそもなぜこんな疑問が生まれたかというと、問題文に「このメソッドは、文字列の要素で構成された配列を受け取り、各要素を連結した後、大文字にして結果を返します。」と書いてあり、 連結する⇨大文字にする の順番で処理をするのでは?と悩んでしまったからだ。
この順番で記述する場合は以下のとおり(引用:Qiita)。
-----------引用ここから---------
>> def yeller(s)
>> s.join.upcase
>> end
=> :yeller
>> yeller(['o','l','d'])
=> "OLD"
-----------引用おわり---------
しかし、問題文にはこう書いてある。
ヒント:
mapとupcaseとjoinメソッドを使ってみましょう。
これはつまり、直前で学んだmap,upcase,joinを使って問題を解いてみようということだ。
そこでこの3つを利用しつつ、問題文の順序で記述する方法を考える。
その① s.map(&:upcase).joinを単純に入れ替えてみる
>> def yeller(s)
>> s.join.map(&:upcase)
>> end
# yeller(["o", "l", "d"]) を実行するとエラーになる
# in `yeller': undefined method `map' for "old":String (NoMethodError)
s.joinで生成されたインスタンス(今回は文字列"old",Stringオブジェクト)にmapというメソッドが定義されてないエラー。
mapはEnumerableモジュールのインスタンスメソッド(Arrayクラスにも定義されている)。Stringクラスには定義されていないのでエラーになる。
(エラー文を読む際、この記事を参考にさせていただきました。あリがとうございます。)
その②Arrayを使って class を Array にする。
>> def yeller(s)
>> Array[s.join].map(&:upcase)
>> end
# yeller(["o", "l", "d"]) を実行すると ["OLD"] が出力される
ここでjoinとmapについておさらいしておく。
join:配列の各要素を結合して、文字列にする
map:配列の各要素にブロックを適用して、配列にする
その②の["OLD"]はArrayで生成された配列をmapが大文字に変換し、出力されたもの。これでは配列のままなので、文字列に変換しなくてはいけない。
「ruby 配列を文字列に」で検索するとjoinを使った方法をオススメされるが、今回は別の方法を模索していく。
調べてみると、配列オブジェクト[数字]で配列から要素を取得できるらしいので、早速実行してみる。(その③)
>> def yeller(s)
>> Array[s.join].map(&:upcase) [0]
>> end
# yeller(["o", "l", "d"]) を実行すると "OLD" が出力される
"OLD"を出力できた。
ここで終わっても良いが、答えの"OLD"と一致しているか念のため調べてみる。
>> def yeller(s)
>> s1 = s.map(&:upcase).join
>> s2 = Array[s.join].map(&:upcase) [0]
>>
>> puts ("#{ s1 }" + " " + "#{ s1.class }")
>> puts ("#{ s2 }" + " " + "#{ s2.class }")
>>
>> if s1 == s2 then
>> puts "same"
>> elsif
>> puts "different"
>> end
>>
>> end
# yeller(["o", "l", "d"]) の実行結果は次のとおり。
# OLD String
# OLD String
# same
どちらとも、 String クラスの文字列"OLD"を出力できた。
このままでは見づらいので、その②の見た目を整えて終了する。
>> def yeller(s)
>> ary = Array[s.join]
>> arymap = ary.map(&:upcase)
>> arymap [0]
>> end
# yeller(["o", "l", "d"]) を実行すると "OLD" が出力される
-
この記事とこの記事を参考にした。原著のRuby on Railsチュートリアルを購入すると演習の解答が見れるらしいが、筆者は購入していない。 ↩