チェリー本 4章 その2
ブロック
超rubyでも出てきたが
いまいちよくわかっていないのが現状です。
ここでしっかり概念を抑えていきたいと思います。
ブロックとは...
メソッドの引数として渡すことができる処理の塊。
ブロック内で記載した処理は必要に応じてよばれる。
ここでは参考として
rubyの特徴を記載します。
繰り返し処理の場合...
他の言語(javaとかCとか)は繰り返し処理は基本的にfor文を使い
プログラムがコンピューターに対して処理を明示的に命令している。
一方でruby
繰り返しメソッド(each、timesとか)を使うのが主流。
もちろんrubyでもfor文は存在する。
ここではeachメソッドを使っています。
eachメソッドの役割は配列の要素を最初から最後まで取り出す作業をしてくれる。
さっき出てきたブロックが do~endのこと。
numbers = [1, 2, 3, 4] sum = 0 numbers.each do |n| sum += n end puts sum => 10
|n|のことををブロック引数と言う。
ここにeachで取り出した1,2,3,4が渡ってくる。
ブロックの引数は使わない場合は省略できる。
ブロックを使用する際は気をつけないといけないことがある。
例えば
このような下のコードが存在する時
この式は
配列を順番に取り出す
ブロック内の処理は
条件演算子で
偶数の場合10倍する、奇数の場合そのまま
それらを最終的に足し算する式が存在する。
numbers = [1, 2, 3, 4] sum = 0 numbers.each do |n| sum_value = n.even? ? n * 10 : n end p sum => 64
ブロックにはスコープという有効範囲が存在する。
仮にスコープ内のsum_valueを呼んでみる。
numbers = [1, 2, 3, 4] sum = 0 numbers.each do |n| sum_value = n.even? ? n * 10 : n p sum_value end #スコープ内の為呼び出しに成功している。 => 1 20 3 40 #スコープ外で呼び出すと numbers = [1, 2, 3, 4] sum = 0 numbers.each do |n| sum_value = n.even? ? n * 10 : n end p sum_value => Traceback (most recent call last): lib/ch_4.rb:8:in `<main>': undefined local variable or method `sum_value' for main:Object (NameError)
スコープの外と中では世界が違う
逆にスコープの外で作られた変数は使える。
ブロック引数の名前をブロックの外にある変数と同じ名前にすると
ブロック内では引数の値が優先して使われる。
名前の重複により他の変数が使えなくなることをシャドーイングという。
混乱を招くので使わない方がいい
do...endと{}
今まではブロックを記載するときは改行を入れていたが一行で書くこともできる。
numbers = [1, 2, 3, 4] sum = 0 numbers.each do |n| sum += n end p sum =>10
が
すごい見にくい
ブロックの記載には
do~endを使う代わりに{}を使って書くこともできる。
numbers = [1, 2, 3, 4] sum = 0 numbers.each { |n| sum += n } p sum => 10 #もちろん改行も使える numbers.each { |n| sum += n } p sum => 10
使い分けは
do...end | {}で記載する時は |
---|---|
改行を含む長いブロックを書くとき | 一行でコンパクトに書きたいとき |
今後もどんなときにブロックが便利か勉強していきます。
チェリー本 4章 その1
配列
配列や繰り返し処理を理解する。
配列は改行して値を代入することができる。
この式は数値1,2,3を変数aに代入するコード
a = [ 1, 2, 3, ] p a => [1, 2, 3]
最後の要素が,ついても文法上エラーにはならない。
配列の中に配列も可能
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] p a[1][0] #4を呼び出せる。
要素の追加と削除
追加
配列には添字を添えて新しい値を代入することができる。
a = [10, 20, 30, 40] p a => [10, 20, 30, 40] #一番最初の10を11に書き換え a[0] = 11 p a => [11, 20, 30, 40] #今現在配列の要素にある添字は0~3までしか存在しない。 #存在しない値を取り出すとnilになる。 #添字にa[6] = 60とするとどうなる?? [11, 20, 30, 40, nil, nil, 60] 添字は4~5はnilで埋められる。
削除
配列の中の要素を削除したい時
a = [10, 20, 30, 40] #配列の中の何を消したかわかる p a.delete_at(1) => 20 #今現在の配列の中がわかる。 p a => [10, 30, 40] 消したい添字番号が存在しないとnilを返す。
配列を使った二重代入
2章で出てきた多重代入
ここではどんな時使えるか書いていく。
a, b = [1, 2] p a, b
今回記載するのは...
rubyで割り算の商と余りを配列にして返してくれる。
メソッドdivmodが存在する。
#変数quo_remにdivmodメソッドの戻り値を代入 quo_rem = 14.divmod(4) #戻り値そのまま受け取る p "商 = #{quo_rem[0]}, 余り = #{quo_rem[1]}" 変数を別々に受け取ることができる。 quotient, remainder = 14.divmod(4) p "商 = #{quotient} 余り = #{remainder}"
チェリー本 3章 その2
前の章で書いたFizzBuzzプログラムをのテストを自動化していく
現状のコード
def fizz_buzz(n) if n % 15 == 0 "Fizz Buzz" elsif n % 3 == 0 "Fizz" elsif n % 5 == 0 "Buzz" else n.to_s end end p fizz_buzz(1) p fizz_buzz(2) p fizz_buzz(3) p fizz_buzz(4) p fizz_buzz(5) p fizz_buzz(6) p fizz_buzz(15) => "1" "2" "Fizz" "4" "Buzz" "Fizz" "Fizz Buzz"
上のコードにテスト環境を入れていく
require "minitest/autorun" class FizzBuzzTest < Minitest::Test #FizzBuzzTestクラスを定義 def test_fizz_buzz #test_fizz_buzzを定義 assert_equal "1", fizz_buzz(1) #fizz_buzzメソッド引数に1を呼んだ時 "1"が表示される?? assert_equal "2", fizz_buzz(2) assert_equal "Fizz", fizz_buzz(3) assert_equal "4", fizz_buzz(4) assert_equal "Buzz", fizz_buzz(5) assert_equal "Fizz", fizz_buzz(6) assert_equal "Fizz Buzz", fizz_buzz(15) end end ####結果 Run options: --seed 64884 # Running: . Finished in 0.001210s, 826.4463 runs/s, 5785.1240 assertions/s. 1 runs, 7 assertions, 0 failures, 0 errors, 0 skips ##テストは通っています。
ちゃんとテストが正しいか失敗してみる。
assert_equal "Fizz Buzz", fizz_buzz(16) #16を渡してみる。 1) Failure: FizzBuzzTest#test_fizz_buzz [fizz_buzz.rb:23]: --- expected +++ actual @@ -1 +1,3 @@ -"Fizz Buzz" +# encoding: US-ASCII +# valid: true +"16"
"Fizz Buzz"が期待した結果ですが
"16"が表示されているからテストは正しいことがわかる。
プログラムとテストを分離する
テストも終わり無事望んだ結果だが今の現状テストとプログラムが一緒になっているので
テストはテスト、プログラムはプログラムと別々に分ける必要がある。
今のファイル構造
fizz_buzz.rb def fizz_buzz(n) if n % 15 == 0 "Fizz Buzz" elsif n % 3 == 0 "Fizz" elsif n % 5 == 0 "Buzz" else n.to_s end
fizz_buzz_test.rb equire "minitest/autorun" class FizzBuzzTest < Minitest::Test def test_fizz_buzz assert_equal "1", fizz_buzz(1) assert_equal "2", fizz_buzz(2) assert_equal "Fizz", fizz_buzz(3) assert_equal "4", fizz_buzz(4) assert_equal "Buzz", fizz_buzz(5) assert_equal "Fizz", fizz_buzz(6) assert_equal "Fizz Buzz", fizz_buzz(15) assert_equal "16", fizz_buzz(16) end end
分離した後
テストを走らせると
エラーになる...
# Running: E Finished in 0.001552s, 644.3299 runs/s, 0.0000 assertions/s. 1) Error: FizzBuzzTest#test_fizz_buzz: NoMethodError: undefined method `fizz_buzz' for #<FizzBuzzTest:0x00007f812d856a00> test/fizz_buzz_test.rb:5:in `test_fizz_buzz' 1 runs, 0 assertions, 0 failures, 1 errors, 0 skips
定義されてないfizz_buzzメソッドがあるよって教えてくれている。
プログラムファイルは別の世界に行ってしまったので取り込む必要がある。
fizz_buzz_test.rbにfizz_buzz.rbを読み込んでいく
require "minitest/autorun" require "./lib/fizz_buzz.rb" class FizzBuzzTest < Minitest::Test def test_fizz_buzz assert_equal "1", fizz_buzz(1) assert_equal "2", fizz_buzz(2) assert_equal "Fizz", fizz_buzz(3) assert_equal "4", fizz_buzz(4) assert_equal "Buzz", fizz_buzz(5) assert_equal "Fizz", fizz_buzz(6) assert_equal "Fizz Buzz", fizz_buzz(15) assert_equal "16", fizz_buzz(16) end end ### テスト通った!!! Run options: --seed 60808 # Running: . Finished in 0.002517s, 397.2984 runs/s, 3178.3870 assertions/s. 1 runs, 8 assertions, 0 failures, 0 errors, 0 skips
3章を通して...
人間が手作業や目視で確認すると時間もかかるし
間違いを犯すことがある
機械に任せるとテストが一瞬で終わるし
間違いを犯すこともない。
何回テストをやらせても文句も言わない。
テストコードを書くには手間がかかるが
その分見返りが大きいので積極的に使って行きたいと思います。
需要はRspecの方があるので
まずはでテストの概念をつかんで後々Rspecをガシガシ書いていきたいと思います!
お疲れ様でした!
第4章も頑張ります!
チェリー本 3章 その1
rubyだけではないがプログラムを書いていく中で必ずミスや今書いてるコードがちゃんと動作するか確認する必要がある。
人間が目視にて指先確認してもいいがコードが膨大になると途方に暮れるぐらい疲れるのでそこをコンピューターにやってもらう
間違いを指摘してくれるしメリットしかない。
テストを自動化してくれるテスティングフレームワークは今回はminitestを利用する。
rubyの標準装備のところもいい
テストを実施する流れ
① テスティングフレームワークにそって検証用のプログラムを書く
② ①の実行する。
③ 検証が正か負か報告する。
基本的なコード
今回のテストする内容は
小文字rubyが大文字RUBYになっているか確認するテスト
require "minitest/autorun" class SampleTest < Minitest::Test def test_sample assert_equal "RUBY", "ruby".upcase end end
require ~ はテストライブラリーの読み込み
class~最後のendmまでがテストクラス(テストの本体)
SampleTestはクラスの名前、命名は自由基本的にテストの場合だとtestが名前に入ることが一般的
ファイル名はクラス名と合わせることが基本 キャメルケースで記載すること。
def ~から対象となるテストメソッドtest_で始めることが必須テストする内容が推測できるようにする。
assert_equal "RUBY", "ruby".upcase
が実行結果を確認する検証メソッドここではminitestが提供しているassert_equal メソッドを使って検証している。
assert_equal 期待する結果,テストする値や式
本書で使用するメソッドは
#aとbが等しいか? assert_equal #aが真か? assert a #aが偽か? refure a
テスト結果の確認方法
Run options: --seed 13685 # Running: . Finished in 0.001080s, 925.9259 runs/s, 925.9259 assertions/s. 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
- Running:の下の.がテストの進捗状況、今回はテスト一つだけなので一個
- Finished~が実行スピード
最後のテスト実行結果のまとめ
- 1:runs :実行したテストメソッドの件数
- 1 assertions 実行した検証メソッドの件数
- 0 failures 検証に失敗したテストメソッドの件数
- 0 errors 検証中にエラーが発生したテストメソッドの件数
- 0 skips skipsメソッドにより実行をスキップしたテストメソッドの件数
failuresとerrors が0であればテストは全部パスしたことになる。
テストの失敗パターン
assert_equal "RUBY", "ruby".capitalize #メソッド内の記載を変更、頭文字だけ大文字 Run options: --seed 61835 # Running: F Finished in 0.001417s, 705.7163 runs/s, 705.7163 assertions/s. 1) Failure: SampleTest#test_sample [fizz_buzz.rb:5]: Expected: "RUBY" Actual: "Ruby" 1 runs, 1 assertions, 1 failures, 0 errors, 0 skips
- まず失敗のFailure: [fizz_buzz.rb:5]:で失敗したことを教えてくれる。
- 成功したときは.だったがFになってる
- Expected: "RUBY" 期待した結果
- Actual: "Ruby" 実際はこれ
テストが失敗した場合そのテストメソッドはそれ以上進まない。
テストメソッドが複数ある場合次に進む
実行中にエラーになった時
検証する前にエラーが出た場合
次のコードはエラーになる。
assert_equal "RUBY", nil.upcase #レシーバーをnilにする。そもそもnilは文字列じゃないからupcaseを呼べない Run options: --seed 18496 # Running: E Finished in 0.001229s, 813.6696 runs/s, 0.0000 assertions/s. 1) Error: SampleTest#test_sample: NoMethodError: undefined method `upcase' for nil:NilClass Did you mean? case fizz_buzz.rb:5:in `test_sample' 1 runs, 0 assertions, 0 failures, 1 errors, 0 skips
- 失敗時はFだったがEになった
- SampleTest#test_sample:でエラーが出た
- nilにupcaseは定義されていない
- fizz_buzz.rb:5:in `test_sample' 5行目でエラー
テストが失敗した時と同じでエラーが起きた場合次のテストメソッドがよばれる。
まとめ
- テストを書くとコンピューターがどこでエラーが出たか親切に教えてくれる。
- テストの結果には成功、失敗、エラーの3つが存在する。
- 失敗、エラーが出たら次のテストメソッドが実行される。
チェリー本 2章 その4
rubyの?で終わるメソッド
Rubyのメソッド名の中には?や!で終わるものが存在する。
?で終わるメソッドは真偽値を返すメソッドになる。
p "".empty? p "abc".empty? #引数に文字列が含まれているか?? p "watch".include?("at") p "watch".include?("in") => true false true false
上のnil?やempty?は最初からrubyで定義されている。
rubyは自分で?で終わるメソッドを定義することが出来る。
真偽値を求める時は?を使うようにする。
def method_name?(x) p x == 1 end method_name?(1) method_name?(2) method_name?(3) => true false false
rubyの3つのライブラリー
組み込みライブラリー
rubyはあらかじめ数多くのライブラリーが標準で搭載されている。
例えばstring(文字列)、integer(数値)がある。
標準ライブラリー
組み込みではにが標準ライブラリー存在する。
Dateクラスとかが該当する。
外部ライブラリー(gem)
有志の開発者が作成してくれた外部ライブラリー
インストール不要
インストール必要
require
標準ライブラリーはインストールは不要だが
標準ライブラリーとgem読み込み必要がある
次のような構文を使います。
require ライブラリー名
Dateクラスを使いたい時は...
require "date" p Date.today => #<Date: 2021-04-26 ((2459331j,0s,0n),+0s,2299161j)> requireないと構文エラー Traceback (most recent call last): fizz_buzz.rb:1:in `<main>': uninitialized constant Date (NameError) Did you mean? Data
require_relative
実行したいファイルのディレクトリからの相対パスで該当のファイルを探しに行くいまいちよくピンと来ないのでぐぐることにしました。
require | require_relative |
---|---|
カレントディレクトリーを示す ./の記載が必要 |
カレントディレクトリを示す ./を付けなくていい |
例えば...
# sample.rbがtest.rb読み込む時 require './test' #./が必要 sample = Sample.new sample.test # test.rb class Sample def test puts '読み込みOK!' end end #一方でrequire_relativeの場合 # sample.rb require_relative 'test' #./省略可能! sample = Sample.new sample.test Copy # test.rb class Sample def test puts '読み込みOK!' end end
1章を通してタメになるところはめちゃめちゃタメになりました。
中には難しい場所もあったけど繰り返しコード書きまくって覚えたいと思います。
チェリー本 2章 その3
fizz_buzzメソッドを作成する!
- 3で割り切れる数値を引数に渡すと”Fizz”を返す
- 5で割り切れる数値を引数に渡すと”Buzz”を返す
- 15で割り切れる数値を引数に渡すと”FizzBuzz”を返す
- それ以外はその数字を文字列で返す。
今までやってきたことのおさらい
def fizz_buzz(n) if n % 15 == 0 #15は3でも5でも割り切れるので一番上に記載すること。 puts "fizz_buzz" elsif n % 5 == 0 puts "Buzz" elsif n % 3 == 0 puts "Fizz" else puts n.to_s end end fizz_buzz(1) fizz_buzz(2) fizz_buzz(3) fizz_buzz(4) fizz_buzz(5) fizz_buzz(6) fizz_buzz(15) => h@poi: ~/ruby-book $ ruby lib/fizz_buzz.rb 1 2 Fizz 4 Buzz Fizz fizz_buzz
fizz_buzzメソッドを作成する箇所は特に難しいと思ったところがないので次に行きます笑
%記法で文字列を作る
%記法を使うと””や’’でエスケープする必要がなくなります。
#%q!!を使った場合 シングルクォートで囲った事になる。 puts %q!hi! =>hi #%Q!!を使った場合 ダブルクォートで囲った事になる。式展開使用可能 x = "大文字Qの場合!" puts %Q!#{x}! => 大文字Qの場合! #%!!もダブルクォートで囲った事になる。
ヒアドキュメント(行思考文字列リテラル)
長い文字列を使う場合スッキリする
a = <<Text ⇦識別子は自由に決めれる。 これはヒアドキュメントです。 複数行あるときに便利です。 Text puts a => h@poi: ~/ruby-book $ ruby lib/fizz_buzz.rb これはヒアドキュメントです。 複数行あるときに便利です。
フォーマットを指定して文字列を作る時
sprintfメソッドを使用すると
少数数第3位まで表示する文字列を作成することができる。
p sprintf("%0.3f", 1.2)
"フォーマット文字列%表示したいオブジェクト"の形式で書いても同じ効果が得ることができる。
p "%0.3f" % 1.2 => "1.200"
ここら辺はちょっと難しいので必要に応じてググります。笑
その他の文字列の作成について
#数字を文字列にする。 123.to_s =>"123" #配列を連結して一つの文字列にする。 p [10, 20, 30].join =>"102030" #String.newを使って新しい文字列を作る(あまり使わないらしい...) p String.new("hello") =>"hello"
rubyは文字と文字列の区別はないらしい...
他の言語は区別するらしいけど rubyは1文字でも2文字でも文字列(String)として扱われる。
真偽値と条件分岐について
&&や||の戻り値と評価を終了するタイミング
&&や||を使用した場合必ずしもtrueやfalseになるとは限らない
p 1 && 2 && 3
例えばこの式
戻り値は3になる
rubyは式全体が真または偽になるまで左辺から順に式を評価していく。
p 1 && 2 && 3の場合
全て真の為
最後に評価した値が帰ってくる。
一方で
p 1 && nil && 3 => nil
の場合だとnilの時点で偽が確定する為
戻り値はnilになる
||の場合でも同じことが言える。
p false || nil || 3 || 2 =>3
orの場合だと真を評価した時点で戻り値がわかる
優先度が低い and ,or ,not
&& や|| や!に近いand,or,notがあるが
今まで特に意識することはなかったが
実は優先度が存在する。
[11] pry(main)> t1 = true => true [12] pry(main)> f1 = false => false [13] pry(main)> t1 and f1 => false [14] pry(main)> t1 or f1 => true [15] pry(main)> not t1 => false
優先度が高い順
高 ! && || not andとor⇦同じ優先度 低
記号と英語の論理演算子を混ぜた場合結果が異なる
[16] pry(main)> !f1 || t1 => true [17] pry(main)> not f1 || t1 => false #次の式と同じことが言える #優先度の関係で結果が異なる。 #!は||よりも優先度が高い !(f1)||t1 #notは||よりも優先度が低い not(f1||t1)
&&、||と異なりandとorは優先度に違いがない
()を使わない場合は左から右に真偽値が評価される。
pry(main)> t1 || t2 && f1 => true [21] pry(main)> t1 or t2 and f1 => false
andとorっていつ使う??
andとorは条件分岐で使うのではなく
制御フローで扱うのに向いている
例えば...
「正常なユーザーであれば、そのユーザーにメールを送信する」といった架空のコードからメソッドを呼び出しに使ったもの
このままだと構文エラーになる。
user.valid? && send_mail_to user #構文エラー (user.valid?) && (end_mail_to) userのように認識されてしますため andを使うとエラーにならない user.valid? and send_mail_to user (user.valid?) and (send_mail_to user)のように制御される為
続く...
チェリー本 2章 その2
rubyの真偽値のルール
falseのルール
- false またはnil
trueのルール
- trueそのもの
- 全ての数字1,2,3とか-1もtrue
- 全ての文字列 "true" ,"false"とかの文字列
論理演算子
&&や||を使うと複雑な条件を一つにまとめることができる。
&&と||は組み合わせて使うとこができる。
条件1 && 条件2 || 条件3 && 条件4
&&は||より優先度が高い為
記載するときは優先度に気をつける。
()を使えばわかりやすくなる。
(条件1 && 条件2) || (条件3 && 条件4)
()で優先度を変えることができる。
条件1 && (条件2 || 条件3) && 条件4
if文
基本的にif文は最後に評価された式を戻り値として返す
n = 11 if n > 10 puts "10より大きい" else puts "10より小さい" end => 10より大きい
if文が戻り値を戻す性質を利用し次のようにif文の戻り値を変数に代入することができる。
country = "italy" greeting = if country == "japan" "こんにちわ" elsif country == "us" "Hello" elsif country == "italy" "ciao" else "???" end p greeting => "ciao"
メソッドの定義
def method_name #基本文 end
メソッドを定義する時の基本的な決まり
・メソッド名はスネークケースで書く
・キャメルケースは使わない(エラーにはならない一般的ではない)
・ _で始まる(エラーにはならない一般的ではない)
・メソッド名に数字を入れる
・数字から始まるメソッド名は使えない(エラーになる)
・メソッド名をひらがなにする(エラーにはならない一般的ではない)
rubyは最後に評価された式がメソッドの戻り値になるのが特徴で
returnは不要
一般的にreturnを書かないのが普通
returnはメソッドを途中で脱出するときに使う。
メソッドに引数を渡すときは()を省略して記載知ることが一般的。