【Railsチュートリアル】第4章 演習問題解答 1/2

【Railsチュートリアル】第4章 演習問題解答 1/2

第4章正直一番つまんないと思いますが、
大事なところなのでがんばりましょう。

演習多いので2部構成です。
ここでは「4.3.3 ハッシュとシンボル」まで

※コンソール必要な問題は[rails c]実行してある前提とします。

目次

4.2.2 文字列

1.city変数に適当な市区町村を、prefecture変数に適当な都道府県を代入してください。

解答

神奈川県横浜市にしてみました。

irb(main):001:0> city = "横浜市"
=> "横浜市"
irb(main):002:0> prefecture = "神奈川県"
=> "神奈川県"

2.先ほど作った変数と式展開を使って、「東京都 新宿区」のような住所の文字列を作ってみましょう。出力にはputsを使ってください。

解答

半角スペースが要るのでそこだけ注意です。

irb(main):003:0> puts city + " " + prefecture
横浜市 神奈川県
=> nil

3.上記の文字列の間にある半角スペースをタブに置き換えてみてください。(ヒント: 改行文字と同じで、タブも特殊文字です)

解答

特殊文字の理解を問う問題ですね。
解答としては一つ目のもの。
二つ目のものはおまけ。改行コードバージョン。

irb(main):004:0> puts city + "\t" + prefecture
横浜市 神奈川県
=> nil
irb(main):005:0> puts city + "\n" + prefecture
横浜市
神奈川県
=> nil

4.タブに置き換えた文字列を、ダブルクォートからシングルクォートに置き換えてみるとどうなるでしょうか?

解答

シングルクォートだと特殊文字として認識しないで文字列として認識するよって問題です。

irb(main):006:0> puts city + '\t' + prefecture
横浜市\t神奈川県
=> nil

4.2.3 オブジェクトとメッセージ受け渡し

1.”racecar” の文字列の長さはいくつですか? lengthメソッドを使って調べてみてください。

解答

lengthメソッド使えばいいですね

irb(main):007:0> "racecar".length
=> 7

2.reverseメソッドを使って、”racecar”の文字列を逆から読むとどうなるか調べてみてください。

解答

この問題正直好きじゃないんですよね。
回文ならぬ回単語(?)なので逆さまになってる実感得られないですよね。
おまけでRuby on Railsをひっくり返してみました。

irb(main):008:0> "racecar".reverse
=> "racecar"
irb(main):009:0> "Ruby on Rails".reverse
=> "sliaR no ybuR"

3.変数sに “racecar” を代入してください。その後、比較演算子 (==) を使って変数sとs.reverseの値が同じであるかどうか、調べてみてください。

解答

このためのracecarなんだろうなぁ。。笑
trueが返ってくることを確かめれば良いですね。

irb(main):010:0> s = "racecar"
=> "racecar"
irb(main):011:0> s == s.reverse
=> true

4.リスト 4.9を実行すると、どんな結果になるでしょうか? 変数sに “onomatopoeia” という文字列を代入するとどうなるでしょうか? ヒント: 上矢印 (またはCtrl-Pコマンド) を使って以前に使ったコマンドを再利用すると一からコマンドを全部打ち込む必要がなくて便利ですよ。)

解答

racecarは回文だからif文通りますが、onomatopeiaは回文でないのでif文を通らないですね。

irb(main):012:0> puts "It's a palindrome!" if s == s.reverse
It's a palindrome!
=> nil
irb(main):013:0> s = "onomatopeia"
=> "onomatopeia"
irb(main):014:0> puts "It's a palindrome!" if s == s.reverse
=> nil

4.2.4 メソッドの定義

1.リスト 4.10のFILL_INの部分を適切なコードに置き換え、回文かどうかをチェックするメソッドを定義してみてください。ヒント: リスト 4.9の比較方法を参考にしてください。

解答

s == s.reverseで回文が確かめられることを利用します。

irb(main):014:0> puts "It's a palindrome!" if s == s.reverse
irb(main):015:0> def palindrome_tester(s)
irb(main):016:1>   if s == s.reverse
irb(main):017:2>     puts "It's a palindrome!"
irb(main):018:2>   else
irb(main):019:2*     puts "It's not a palindrome."
irb(main):020:2>   end
irb(main):021:1> end
=> :palindrome_tester

これを確かめる問題が次の問題です。

2.上で定義したメソッドを使って “racecar” と “onomatopoeia” が回文かどうかを確かめてみてください。1つ目は回文である、2つ目は回文でない、という結果になれば成功です。

解答

というわけで確かめてみましょう。
先ほど定義した関数に引数で文字列を渡してあげれば良いですね。

irb(main):022:0> #期待結果は"It's a palindrome!"
irb(main):023:0* palindrome_tester("racecar")
It's a palindrome!
=> nil
irb(main):024:0> #期待結果は"It's not a palindrome."
irb(main):025:0* palindrome_tester("onomatopoeia")
It's not a palindrome.
=> nil

3.palindrome_tester(“racecar”)に対してnil?メソッドを呼び出し、戻り値がnilであるかどうかを確認してみてください (つまりnil?を呼び出した結果がtrueであることを確認してください)。このメソッドチェーンは、nil?メソッドがリスト 4.10の戻り値を受け取り、その結果を返しているという意味になります。

解答

これは一体何してるかというと関数の戻り値があるかどうかを調べてるんですね。
nil?なので戻り値がなければtrueだし、戻り値があればfalseになります。
この関数では文字列を標準出力しているだけで戻り値を用意していないので、結果はtrueになります。

irb(main):026:0> palindrome_tester("racecar").nil?
It's a palindrome!
=> true

4.3.1 配列と範囲演算子

1.文字列 “A man, a plan, a canal, Panama” を “, ” で分割して配列にし、変数aに代入してみてください。

解答

カンマ3つあるから配列の要素は4つになりそうですね。

irb(main):027:0> a = "A man, a plan, a canal, Panama".split(",")
=> ["A man", " a plan", " a canal", " Panama"]

ちなみにaだけ打ってやれば代入されていることも確認できます。

irb(main):028:0> a
=> ["A man", " a plan", " a canal", " Panama"]

2.今度は、変数aの要素を連結した結果 (文字列) を、変数sに代入してみてください。

解答

配列の連結にはjoinメソッドを使います。
実際に代入されたことを確認するところまでやってみます。

irb(main):034:0> s = a.join
=> "A man a plan a canal Panama"
irb(main):035:0> s
=> "A man a plan a canal Panama"

3.変数sを半角スペースで分割した後、もう一度連結して文字列にしてください (ヒント: メソッドチェーンを使うと1行でもできます)。リスト 4.10で使った回文をチェックするメソッドを使って、(現状ではまだ) 変数sが回文ではないことを確認してください。downcaseメソッドを使って、s.downcaseは回文であることを確認してください。

解答

メソッドってどんどん繋げていけるんですよね。
せっかくなので途中経過を確認しながら一行で書いてみたいと思います。

downcaseがないと回文ではない判定されることと、あれば回文判定することを確かめます。

=> "A man a plan a canal Panama"
irb(main):036:0> s.split
=> ["A", "man", "a", "plan", "a", "canal", "Panama"]
irb(main):037:0> s
=> "A man a plan a canal Panama"
irb(main):038:0> s.split.join
=> "AmanaplanacanalPanama"
irb(main):039:0> palindrome_tester(s.split.join)
It's not a palindrome.
=> nil
irb(main):040:0> palindrome_tester(s.split.join.downcase)
It's a palindrome!
=> nil

4.aからzまでの範囲オブジェクトを作成し、7番目の要素を取り出してみてください。同様にして、後ろから7番目の要素を取り出してみてください。(ヒント: 範囲オブジェクトを配列に変換するのを忘れないでください)

解答

配列にするのを忘れるとしょぼい結果になります。
ちゃんとto_aを使用してあげます。

irb(main):045:0> ('a'..'z')
=> "a".."z"
irb(main):046:0> ('a'..'z').to_a
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]

7番目の要素がほしいので配列は0から始まることを考慮すると6を指定すれば良いことになります。

irb(main):047:0> ('a'..'z').to_a[6]
=> "g"

4.3.2 ブロック

1.範囲オブジェクト0..16を使って、各要素の2乗を出力してください。

解答

mapを使用します。
mapってややこしいけど、慣れるとeachで複数行またがるところが一行でスッキリ書けたりします。

irb(main):050:0> (0..16).map {|n| n**2}
=> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256]

eachをつかうこともできまっせ

irb(main):065:0> (0..16).each do |n|
irb(main):066:1* puts n**2
irb(main):067:1> end
0
1
4
9
16
25
36
49
64
81
100
121
144
169
196
225
256
=> 0..16

出力の仕方は問われてないので、簡単にputs使いました。

2.yeller (大声で叫ぶ) というメソッドを定義してください。このメソッドは、文字列の要素で構成された配列を受け取り、各要素を連結した後、大文字にして結果を返します。例えばyeller([’o’, ’l’, ’d’])と実行したとき、”OLD”という結果が返ってくれば成功です。ヒント: mapとupcaseとjoinメソッドを使ってみましょう。

解答

機能の仕様を満たすだけならmapいらないんじゃないかとも思うのですが…

irb(main):068:0> def yeller(voice_array)
irb(main):069:1> voice = voice_array.join.upcase
irb(main):070:1> puts voice
irb(main):071:1> end
=> :yeller
irb(main):073:0> yeller(['o','l','d'])
OLD
=> nil

せっかくなので使ってみますか。
mapの中でupcaseして、それをjoinするんだろうけど
そうすると大文字にしてから連結になっちゃうんだよなぁ…

map使って連結してから大文字にする方法知ってる人いたら教えてください。
私の方ではひとまずこちらを解答とさせていただきます。

irb(main):096:0> def yeller(voice_array)
irb(main):097:1> voice = voice_array.map { |c| c.upcase }.join
irb(main):098:1> puts voice
irb(main):099:1> end
=> :yeller
irb(main):100:0> yeller(['o','l','d'])
OLD
=> nil

3.random_subdomainというメソッドを定義してください。このメソッドはランダムな8文字を生成し、文字列として返します。ヒント: サブドメインを作るときに使ったRubyコードをメソッド化したものです。

解答

これもランダムな文字列に使用する文字が指定されていないので条件不十分だなーとか
思いながら解いてたのですが、勝手にa..zの範囲オブジェクトを使用して
アルファベットの文字列を作ることにしました。

  1. 範囲オブジェクトを配列にする
  2. ランダムに7要素取り出す
  3. joinで文字列にする

この流れができていれば良いでしょう。

irb(main):119:0> def random_subdomain
irb(main):120:1> puts ('a'..'z').to_a.shuffle[0..7].join
irb(main):121:1> end
=> :random_subdomain
irb(main):122:0> random_subdomain
qdrlmnak
=> nil

4.リスト 4.12の「?」の部分を、それぞれ適切なメソッドに置き換えてみてください。ヒント:split、shuffle、joinメソッドを組み合わせると、メソッドに渡された文字列 (引数) をシャッフルさせることができます。

解答

  1. 1文字ずつsplitする
  2. shuffleする
  3. joinして文字列とする

この流れが読み取れればもうわかると思います。

irb(main):123:0> def string_shuffle(s)
irb(main):124:1> s.split('').shuffle.join
irb(main):125:1> end
=> :string_shuffle
irb(main):126:0> string_shuffle("foobar")
=> "orfbao"

4.3.3 ハッシュとシンボル

ハッシュってややこしく感じたりしますが、自分は
○○の××は□□だ
を言いたい時に使うやつだと思うことにしています。

1.キーが’one’、’two’、’three’となっていて、それぞれの値が’uno’、’dos’、’tres’となっているハッシュを作ってみてください。その後、ハッシュの各要素をみて、それぞれのキーと値を”’#{key}’のスペイン語は’#{value}’”といった形で出力してみてください。

解答

ハッシュを定義してからeachで回して
keyとvalueをそれぞれ出力させてやれば良いですね。

irb(main):127:0> numbers = {one: "uno", two: "dos", three: "tres"}
irb(main):137:0> numbers.each do |key,value|
irb(main):138:1* puts "#{key}のスペイン語は#{value}"
irb(main):139:1> end
oneのスペイン語はuno
twoのスペイン語はdos
threeのスペイン語はtres

2.person1、person2、person3という3つのハッシュを作成し、それぞれのハッシュに:firstと:lastキーを追加し、適当な値 (名前など) を入力してください。その後、次のようなparamsというハッシュのハッシュを作ってみてください。1.) キーparams[:father]の値にperson1を代入、2). キーparams[:mother]の値にperson2を代入、3). キーparams[:child]の値にperson3を代入。最後に、ハッシュのハッシュを調べていき、正しい値になっているか確かめてみてください。(例えばparams[:father][:first]がperson1[:first]と一致しているか確かめてみてください)

解答

まずハッシュを3つ適当に作ります。
イチローさんとカリスマブラザーズのジローさんと北島三郎さんに協力頂くことにします。

irb(main):141:0> person1= {first: "TANAKA",last: "ICHIRO"}
=> {:first=>"TANAKA", :last=>"ICHIRO"}
irb(main):142:0> person2= {first: "SUZUKI",last: "JIRO"}
=> {:first=>"SUZUKI", :last=>"JIRO"}
irb(main):143:0> person1= {first: "ICHIRO",last: "SUZUKI"}
=> {:first=>"ICHIRO", :last=>"SUZUKI"}
irb(main):144:0> person2= {first: "JIRO",last: "KARISUMA"}
=> {:first=>"JIRO", :last=>"KARISUMA"}
irb(main):145:0> person3= {first: "SABURO",last: "KITAJIMA"}
=> {:first=>"SABURO", :last=>"KITAJIMA"}

最初に空のハッシュ作ってあげないと先にすすめないですよー
(ジローがお母さんになってしまいましたが、深く気にしないでください。。)

irb(main):147:0> params={}
=> {}
irb(main):148:0> params[:father] = person1
=> {:first=>"ICHIRO", :last=>"SUZUKI"}
irb(main):149:0> params[:mother] = person2
=> {:first=>"JIRO", :last=>"KARISUMA"}
irb(main):150:0> params[:child] = person3
=> {:first=>"SABURO", :last=>"KITAJIMA"}

簡単に確かめてみますか。
問題の通り調べていくとICHIROになってます。

irb(main):151:0> params[:father][:first]
=> "ICHIRO"
irb(main):152:0> person1[:first]
=> "ICHIRO"

3.userというハッシュを定義してみてください。このハッシュは3つのキー:name、:email、:password_digestを持っていて、それぞれの値にあなたの名前、あなたのメールアドレス、そして16文字からなるランダムな文字列が代入されています。

解答

またランダム文字列出てきたのでチュートリアルで紹介されたレベルでの書き方ですが..

irb(main):153:0> user = {name: "kojima",email:"kojima@email.com",password_digest:('a'..'z').to_a.shuffle[0..15].join}
=> {:name=>"kojima", :email=>"kojima@email.com", :password_digest=>"qtkwsmceavjuhrzx"}
irb(main):154:0> user[:name]
=> "kojima"
irb(main):155:0> user[:email]
=> "kojima@email.com"
irb(main):156:0> user[:password_digest]
=> "qtkwsmceavjuhrzx"

password_digestが半角英数字全般で重複ありだったらロジック変えないといけないですけどね。
ちゃんと値が入っているところまで確かめてみました。

4.Ruby API (訳注: もしくはるりまサーチ) を使って、Hashクラスのmergeメソッドについて調べてみてください。次のコードを実行せずに、どのような結果が返ってくるか推測できますか? 推測できたら、実際にコードを実行して推測があっていたか確認してみましょう。

解答

最初の予想:a=>100,b=>300となる。
これは自分の予想です。

るりまサーチでは
「selfとotherのハッシュの内容をマージ(統合)した結果を返します。デフォルト値はselfの設定のままです。」
とあります。

読んでからの予想:最初に同じ
統合するので、mergeの中のものは優先して反映されるってことなのでしょう。

じゃあやってみる

irb(main):157:0> { "a" => 100, "b" => 200 }.merge({ "b" => 300 })
=> {"a"=>100, "b"=>300}

あってましたね。めでたしめでたし。

続きはこちら!
【Railsチュートリアル】第4章 演習問題解答 2/2


Railsチュートリアルカテゴリの最新記事