saran12345678 / RubyonRailsTutorial

0 stars 1 forks source link

第4章 Rails風味のRuby #4

Open saran12345678 opened 2 weeks ago

saran12345678 commented 2 weeks ago

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

irb(main):002> city = "名古屋市"
=> "名古屋市"
irb(main):003> city
=> "名古屋市"
irb(main):004> prefecture = "愛知県"
=> "愛知県"
irb(main):005> "#{city} #{prefecture}"
=> "名古屋市 愛知県"

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

irb(main):012> city + " " + prefecture
=> "名古屋市 愛知県"

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

irb(main):010> puts "#{city}\t#{prefecture}"
名古屋市        愛知県
=> nil

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

エスケープされるため、タブではなく文字列として出力される。

irb(main):011> puts '#{city}\t#{prefecture}'
#{city}\t#{prefecture}
=> nil
saran12345678 commented 2 weeks ago

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

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

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

irb(main):017> carName = "racecar"
=> "racecar"
irb(main):019> carName.reverse
=> "racecar"

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

irb(main):020> s = "racecar"
=> "racecar"
irb(main):021> s == s.reverse
=> true

リスト 4.8を実行すると、どんな結果になるでしょうか? 変数sに "onomatopoeia" という文字列を代入するとどうなるでしょうか?(ヒント: 上矢印、またはCtrl-Pコマンドを使って以前に使ったコマンドを再利用すれば、コマンドを全部入力せずに済むので便利です。)

リスト4.8 >> puts "It's a palindrome!" if s == s.reverse

リバースしても同じ文字列であるためTrueとなりPutsが実行される

irb(main):023> puts "It's a palindrome!" if s == s.reverse
It's a palindrome!
=> nil

変数sの値を更新すると、リバース時に別の文字列になってしまうためputsが実行されなくなる。

irb(main):024> s = "onomatopoeia"
=> "onomatopoeia"
irb(main):025> puts "It's a palindrome!" if s == s.reverse
=> nil
saran12345678 commented 2 weeks ago

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

リスト4.9

>> def palindrome_tester(s)
>>   if (コードを書き込む)
>>     puts "It's a palindrome!"
>>   else
>>     puts "It's not a palindrome."
>>   end
>> end
irb(main):036* def palindrome_tester(s)
irb(main):037*   if s == s.reverse
irb(main):038*     puts "It's a palindrome!"
irb(main):039*   else
irb(main):040*     puts "It's not a palindrome."
irb(main):041*   end
irb(main):042> end
=> :palindrome_tester

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

irb(main):043> palindrome_tester("racecar")
It's a palindrome!
=> nil
irb(main):044> palindrome_tester("onomatopoeia")
It's not a palindrome.
=> nil

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

trueが返るため、palindrome_testerの戻り値がnilだとわかる。

irb(main):045> palindrome_tester("racecar").nil?
It's a palindrome!
=> true
saran12345678 commented 2 weeks ago

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

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

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

irb(main):047> s = a.join
=> "A mana plana canalPanama"

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

irb(main):050> palindrome_tester(s.split(" ").join)
It's not a palindrome.
=> nil
irb(main):051> palindrome_tester(s.split(" ").join.downcase)
It's a palindrome!
=> nil

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

先頭からの場合はゼロオリジンを意識する。 逆に語尾からの場合は、−1から始まるため-6ではなく-7が正しい。

irb(main):056> alphaArr = ('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"]
irb(main):057> alphaArr[6]
=> "g"
irb(main):058> alphaArr[-7]
=> "t"
saran12345678 commented 2 weeks ago

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

irb(main):059> (0..16).each {|i| puts i**2}
0
1
4
9
16
25
36
49
64
81
100
121
144
169
196
225
256
=> 0..16

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

irb(main):001* def yeller(s)
irb(main):002*   return s.map(&:upcase).join
irb(main):003> end
=> :yeller
irb(main):004> yeller(['o', 'l', 'd'])
=> "OLD"

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

リスト4.11

>> def string_shuffle(s)
>>   s.?('').?.?
>> end
>> string_shuffle("foobar")
=> "oobfra"
irb(main):005* def string_shuffle(s)
irb(main):006*   s.split('').shuffle.join
irb(main):007> end
=> :string_shuffle
irb(main):008> string_shuffle("foobar")
=> "aoofrb"
saran12345678 commented 2 weeks ago

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

irb(main):009> hash = {"one":"uno", "two":"dos", "three":"tres"}
=> {:one=>"uno", :two=>"dos", :three=>"tres"}
irb(main):019* hash.each do |key, value|
irb(main):020*   puts "#{key.inspect}はスペイン語で#{value.inspect}"
irb(main):021> end
:oneはスペイン語で"uno"
:twoはスペイン語で"dos"
:threeはスペイン語で"tres"
=> {:one=>"uno", :two=>"dos", :three=>"tres"}

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

irb(main):001> person = {first:"yama", last:"taro"}
=> {:first=>"yama", :last=>"taro"}
irb(main):002> person1 = {first:"yama", last:"taro"}
=> {:first=>"yama", :last=>"taro"}
irb(main):003> person2 = {first:"kawa", last:"jiro"}
=> {:first=>"kawa", :last=>"jiro"}
irb(main):004> person3 = {first:"umi", last:"third"}
=> {:first=>"umi", :last=>"third"}
irb(main):005> params = []
=> []
irb(main):006> params = {}
=> {}
irb(main):007> params[:father] = person1
=> {:first=>"yama", :last=>"taro"}
irb(main):008> params[:mother] = person2
=> {:first=>"kawa", :last=>"jiro"}
irb(main):009> params[:child] = person3
=> {:first=>"umi", :last=>"third"}
irb(main):010> params[:father][:first] == person1[:first]
=> true
irb(main):011> params[:father] == person1
=> true

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

irb(main):012> user = {name:"toby", email:"testAddress", password_digest:"****"}
=> {:name=>"toby", :email=>"testAddress", :password_digest=>"****"}

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

次のコード

 { "a" => 100, "b" => 200 }.merge({ "b" => 300 })

マージされることでKey:bが300となるため、{ "a":"100", "b":"300" }となる

{"a"=>100, "b"=>300}
saran12345678 commented 2 weeks ago

RubyのHashオブジェクトのキーには、「1」や「3」といった数字や、「'email'」や「'name'」といった文字列、そして「:email」や「:name」といったシンボルなどが使えますが、特にシンボルが多く使われています。その理由を考えてみましょう20

変数に対しシンボルはメモリが重複しない。 また、整数値で比較が行われるため変数に対して高速になる。

先ほど考えたハッシュのキーにおける「数字 vs. 文字列 vs. シンボル」の比較を、WebブラウザからRubyのコードを実行できるirb.wasmを使って、下記のコードを実行して測定してみましょう。

下記のコード

    require 'benchmark'
    symbol = { :foo => "value" } 
    string = { "foo" => "value" }
    integer = { 1 => "value" } 
    Benchmark.benchmark do |x|
      x.report("Symbol") { symbol[:foo] }
      x.report("String") { string["foo"] }
      x.report("Integer") { integer[1] }
    end

結果

[#<Benchmark::Tms:0x0430b2c8
  @cstime=0.0,
  @cutime=0.0,
  @label="Symbol",
  @real=5.000000001587068e-06,
  @stime=0.0,
  @total=2.0000000006348273e-05,
  @utime=2.0000000006348273e-05>,
 #<Benchmark::Tms:0x0430af30
  @cstime=0.0,
  @cutime=0.0,
  @label="String",
  @real=5.000000001587068e-06,
  @stime=0.0,
  @total=9.999999988963282e-06,
  @utime=9.999999988963282e-06>,
 #<Benchmark::Tms:0x0430aa08
  @cstime=0.0,
  @cutime=0.0,
  @label="Integer",
  @real=5.000000001587068e-06,
  @stime=0.0,
  @total=5.000000001587068e-06,
  @utime=5.000000001587068e-06>]
saran12345678 commented 2 weeks ago

1から10までの範囲オブジェクトを、リテラルを使って生成してみましょう。(復習です)

irb(main):002> a = 1..10
=> 1..10
irb(main):003* a.each do |i| puts i
irb(main):004> end
1
2
3
4
5
6
7
8
9
10
=> 1..10

1から10までの範囲オブジェクトを、コンストラクタを使って生成してみましょう。(ヒント: Rangeクラスのコンストラクタでは2つの引数を渡す必要があります)

irb(main):006> r = Range.new(1, 10)
=> 1..10

比較演算子「==メソッド」を使って、上記2つの課題で作ったそれぞれのオブジェクトの内容が同じであることを確認してみましょう。

irb(main):001> a = 1..10
=> 1..10
irb(main):002> r = Range.new(1, 10)
=> 1..10
irb(main):003> a == r
=> true
saran12345678 commented 2 weeks ago

Rangeクラスの継承階層を調べてみましょう。同様にして、HashとSymbolクラスの継承階層も調べてみましょう。

irb(main):011> r = Range.new(1, 2)
=> 1..2
irb(main):012> r.class
=> Range
irb(main):013> r.class.superclass
=> Object
irb(main):014> r.class.superclass.superclass
=> BasicObject
irb(main):015> r.class.superclass.superclass.superclass
=> nil

Objectの親クラスはRangeにて確認済みのため省略

irb(main):016> h = Hash.new
=> {}
irb(main):017> h.class
=> Hash
irb(main):018> h.class.superclass
=> Object
irb(main):021> s = :symbol
=> :symbol
irb(main):022> s.class
=> Symbol
irb(main):023> s.class.superclass
=> Object

リスト 4.14にあるself.reverseのselfを省略し、reverseと書いてもうまく動くことを確認してみてください。

irb(main):024* class Word < String
irb(main):025*   def palindrome?
irb(main):026*     self == reverse
irb(main):027*   end
irb(main):028> end
=> :palindrome?
irb(main):029> word = Word.new("abcdedcba")
=> "abcdedcba"
irb(main):030> word.palindrome?
=> true

"foobar"オブジェクトのクラスはStringクラスです。これは"foobar".classを実行すると確認できます。そしてRubyではあらゆるものがオブジェクトであるため、このStringクラスでさえも、1つのオブジェクトです。であれば、Stringを生成した何らかのクラスもあるはずです。String.classを実行し、クラスを生成するクラスについて調べみましょう。また、そのクラスの仕様をRubyリファレンスマニュアルから確認してみましょう。(ヒント:クラスの名前が分かったらRubyのクラス一覧のページから探してみましょう)

irb(main):032> String.class.superclass
=> Module

https://docs.ruby-lang.org/ja/latest/class/Module.html

saran12345678 commented 2 weeks ago

palindrome?メソッドを使って、“racecar”が回文であり、“onomatopoeia”が回文でないことを確認してみてください。南インドの言葉「Malayalam」は回文でしょうか? (ヒント: downcaseメソッドで小文字にすることをお忘れなく。)

irb(main):033> word = Word.new("racecar")
=> "racecar"
irb(main):034> word.palindrome?
=> true
irb(main):035> word = Word.new("onomatopoeia")
=> "onomatopoeia"
irb(main):036> word.palindrome?
=> false
irb(main):037> word = Word.new("Malayalam".downcase)
=> "malayalam"
irb(main):038> word.palindrome?
=> true

リスト 4.15を参考に、Stringクラスにshuffleメソッドを追加してみてください。(ヒント: リスト 4.11も参考になります。)

リスト4.15

>> class String
>>   def shuffle
>>     self.?('').?.?
>>   end
>> end
>> "foobar".shuffle
=> "borafo"
irb(main):039* class String
irb(main):040*   def shuffle
irb(main):041*     self.split("").shuffle.join
irb(main):042*   end
irb(main):043> end
=> :shuffle
irb(main):044> "foobar".shuffle
=> "farboo"

リスト 4.15のコードにおいて、self.を削除してもうまく動くことを確認してください。

irb(main):045* class String
irb(main):046*   def shuffle
irb(main):047*     split("").shuffle.join
irb(main):048*   end
irb(main):049> end
=> :shuffle
irb(main):050> "foobar".shuffle
=> "fobaor"
saran12345678 commented 2 weeks ago

第2章で作ったToyアプリケーションのディレクトリでRailsコンソールを開き、User.newと実行することでuserオブジェクトが生成できることを確認してみましょう。

irb(main):001> user = User.new
=> #<User:0x00006fff671ac238 id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>

生成したuserオブジェクトのクラスの継承階層を調べてみてください。

irb(main):003> user.class.superclass
=> ApplicationRecord(abstract)
irb(main):004> user.class.superclass.superclass
=> ActiveRecord::Base
irb(main):005> user.class.superclass.superclass.superclass
=> Object
saran12345678 commented 2 weeks ago

Userクラスで定義されているname属性を修正して、first_name属性とlast_name属性に分割してみましょう。また、それらの属性を使って "Michael Hartl" といった文字列を返すfull_nameメソッドを定義してみてください。最後に、formatted_emailメソッドの@nameの部分を、full_nameに置き換えてみましょう(元々の結果と同じになっていれば成功です)

irb(main):007> example.first_name
=> nil
irb(main):008> example.first_name = "abc"
=> "abc"
irb(main):009> example.last_name
=> nil
irb(main):010> example.last_name = "def"
=> "def"
irb(main):011> example.email = "example.com"
=> "example.com"
irb(main):012> example.formatted_email
=> "abc def <example.com>"

"Hartl, Michael" といったフォーマット(苗字と名前がカンマ+半角スペースで区切られている文字列)で返すalphabetical_nameメソッドを定義してみましょう。

irb(main):007> user.first_name = "first"
=> "first"
irb(main):008> user.last_name = "last"
=> "last"
irb(main):009> user.email = "userEmail"
=> "userEmail"
irb(main):011> user.alphabetical_name
=> "last, first"

full_name.splitとalphabetical_name.split(', ').reverseの結果を比較し、同じ結果になるかどうか確認してみましょう。

irb(main):012> user.full_name.split == user.alphabetical_name.split(', ').reverse
=> true