読了: 2024-05-05
書籍情報
通称チェリー本
Ruby 更新情報
本書の対象バージョンが 3.0 なので 3.1 以降の差分を確認したい
著者によるまとめ:
感想
/icons/hr.icon
第1章 本書を読み進める前に
第2章 Ruby の基礎を理解する
メソッド呼び出し
. 記法は全部呼び出しになる
括弧はあってもなくても同じ
code:ruby
# 下の二つは同じ意味
foo.bar(1, 2, 3)
foo.bar 1, 2, 3
# 下の二つは同じ意味
foo.to_s()
foo.to_s
括弧なしの時はメソッド名の後に空白が要るので括弧をつける時はメソッド名の後に空白を入れてはいけない
code:ruby
# def add(a, b) ... に対して
add(1, 2) # OK
add 1, 2 # OK
add (1, 2) # ERROR: 一つ目の引数に (1, 2) という値を入れようとして syntax error になる感じか?
文字列
式展開: #{}
double quote の方だけ
数値
Rational にする
literal: 0.1r
variable: foo.ratinalize
BigDecimal もある
Boolean
Truthiness: false と nil 以外全部 truthy
メソッド定義
code:ruby
def foo(a, b)
# a と b で何かする
end
括弧は省略できる
引数があるときは括弧をつける方がメジャー
引数がないときは括弧をつけない方がメジャー
参照
オブジェクト参照の確認: foo.object_id
同一オブジェクトかの確認: foo.equal?(bar)
Java と逆 (== と equal の関係が)
require と require_relative
外のプログラムを引き込むやつ
gem で取ってきたやつは require
同じ project 内の他の Ruby ファイルを引き込みたいときは require_relative にすべき
この用途で require を使うときは絶対パス or 相対パスで対象の指定ができるが、相対パスの場合 ruby を実行する current directory に対しての相対パスとして解釈されるので意図しない挙動が発生する危険性がある
第3章 テストを自動化する
他の言語触ったことある想定の本で「おまじない」はどうなの?
まあみんながみんな class based な OOPL 経験があるわけではないということかしらん
何か LSP の suggestion に従って "new Minitest test" でテンプレート出すと require 'test_helper' ってのが追加されてそのまま実行すると LoadError になるんだけど
code:text
<internal:/Users/nash/.local/share/mise/installs/ruby/3.3.0/lib/ruby/3.3.0/rubygems/core_ext/kernel_require.rb>:127:in `require': cannot load such file -- test_helper (LoadError)
from <internal:/Users/nash/.local/share/mise/installs/ruby/3.3.0/lib/ruby/3.3.0/rubygems/core_ext/kernel_require.rb>:127:in `require'
from lib/fizz_buzz.rb:14:in `<main>'
Minitest のデフォルト設定を記入するためのファイルで自分で作って配置するものっぽい
公式 doc でも特に記述が見つけられなかったし慣習か?
第4章 配列や繰り返し処理を理解する
存在しない index へのアクセス -> nil
配列の大きさよりもより大きい index に対する set -> 間が nil で埋められる
delete_at(index): 指定した index の要素を削除
削除した要素を返却
範囲外の index を指定 -> nil を返却
nil な index の削除と区別つかないの気になる、気にならない?
多重代入の右辺を配列にすると展開して代入してくれる
a, b = [1, 2, 3] # a => 1, b => 2, 3 は捨てられる
ブロックパラメータ
code:ruby
foo.each do |a|
# ...
end
# 以下のような書き方もできる、1行だけの処理の時はこっちを使うこともあるとのこと
foo.each { |a| ... }
index の指定
負数で末尾からのアクセスができる (-1 が最後、そこから前に進む)
正数の時と違って out of bound error が起こる
a[index, length] と指定すると index から length 分の部分配列を取り出せる
code:ruby
length で取得するのは後ろ向きのみ
負数を指定しても前方向に取得したりはしない、長さがマイナスということで nil が返ってくる
開始地点から末尾までの長さより大きい値を length に指定しても末尾までの部分配列として返される
first, last
最初・最後の要素を取得する
引数に数値を入れると最初・最後から n 個分の部分配列を返す
これも配列の長さより大きい値を渡しても配列の長さ分までしか返さない
要素の削除
a.delete(e): e に等しい要素を全部削除する
a.delete_at(i) i 番目の要素を削除する
あれこれできすぎでは感
書き方の多様性ってやつ?
while / until
unless では感ある
「condition が true になるまで」ってことか?
第5章 ハッシュやシンボルを理解する
Hash の基本的な記法は <key> => <value> だが、 key を Symbol にする時は <key>: <value> と書ける
この時 key となる Symbol の先頭に : がつかない
メソッドで引数キーワードを使えるようにするには引数名の末尾に : をつける
def foo(a, b:, c: false) = ...
キーワード引数のみデフォルト値を持てる
キーワード引数と Symbol
メソッド定義におけるキーワード引数は Symbol ではない
引数呼び出しは foo
メソッド呼び出しによる引数指定は Symbol
指定するときに :foo => "bar" という書き方もできる
** で Hash を展開する
Hash literal 内で展開して flatten する
メソッド呼び出しで展開してキーワード引数と結びつける
定義側のメソッド引数に ** をつけると任意の任意の Hash を受けられる
Null-safe method call: &.
呼び出し元オブジェクトが nil だったら nil を返す
?. じゃないんだ
名前は safe navigation operator
Idiom: Nil guard (||=)
変数が falsy な時だけ代入する
code:ruby
foo ||= "some value"
以下の記述の省略形
code:ruby
foo || foo = "some value"
値を boolean 化する idiom !!
JS とおなじだな
第6章 正規表現を理解する
match と =~ operator
match は MatchData を返す
=~ はマッチした position を返す
左辺が名前付きキャプチャ入りの regex literal だった場合、キャプチャの名前のローカル変数として値が割り当てられる
regex を変数に代入してから使っても割当が発生しないので注意
Rexexp.last_match: これまでに実行した正規表現マッチングで最後にマッチした結果 (MatchData) が取れる
組み込み変数の S~ と同じ?
match だと組み込み変数に影響を与えるが match? だと書き換えない
単に true/false を返すだけ
docs
第7章 クラスの作成を理解する
attr_reader といえば
Constructor と instance 変数、 accessor methods:
code:ruby
class Foo
# Constructor
def initialize(a, b)
# ...
end
# Getter method
def var
# Instance variable (variables that begin with @)
@var
end
# Setter method
def var=(value)
@var = value
end
end
Foo.new で instance 作る
Instance 変数は初期化してなくてもアクセス可能 (nil が返る)
メソッド名の末尾を = にすると代入する見たいな呼び出し方で値のセットができる
Instance 変数への accessor methods をいちいち自分で書きたくない -> attr_* で定義する
attr_reader: read-only
attr_writer: write-only
attr_accessor: read/write
code:ruby
class Bar
attr_accessor :var, :prop
def initialize(var, prop)
@var = var
@prop = prop
end
end
b = Bar.new("aaa", false)
b.var # => "aaa"
b.prop = true
b.prop # => true
同名の class を複数回定義すると、置き換えられるのではなく上書きされる形で定義が重ね合わせられる
Subclass 関連
super(<arg>)
super method の呼び出し
super method と subclass の method の引数の数が同じ場合は super とだけ書くことで引数を引き継がせることができる
super() にすると引数0の super method を呼ぶという意味になるので挙動が変わってしまう
private method
subclass からも呼べる
override できる
override された時に可視性を返ることができる
怖いね
対策は?
気をつけよう!
は?
class method を parivate にする
class << self の中で private の後に記述
code:ruby
class Foo
class << self
# ...
private
def private_method
'private method dayo'
end
end
end
定義後に private_class+method :<method_name> で定義の変更
private
method のひとつ
定義済みのメソッド名を symbol として引数に与えると、それらのメソッドの可視性を private にする
このときは呼び出し以降に定義された method は private method にならない
なんだ private def foo みたいな定義もできるじゃん
attr_accessor も private 化できる
private attr_accessor :foo, :bar みたいな
ruby 3.0 以降
protected
自 class
subclass 内でレシーバ付き
?
対象 class 内なら他のインスタンスでもアクセス可能
第8章 モジュールを理解する
class 内で include <Module> すると class のメソッドとして module で定義したメソッドが使える
Mixin と呼んでる
module 内でのメソッド定義と可視性の記述は class と同じっぽい
module のメソッド実行時の self は include した class のインスタンスになる
duck typing 的に module 内で定義していない変数を要求することができる
実装読まないと interface わからないの嫌なんだが
include じゃなくて extend にすると module の定義が class method になる
each_char は引数無しで呼ぶと Enumerator を返す
名前空間として module を使用する
module の中に nested class として定義する
code:ruby
module Foo
class Bar # -> Foo::Bar
end
end
class の名前に名前空間として使う module を含めて定義する
code:ruby
module Foo
end
class Foo::Bar
end
参照の探索順位は意味上のではなく記述上の階層に従うので、同名の参照を探す時には上記2つでは異なる参照を使用する可能性がある
code:ruby
# e.g. File (組み込みライブラリに存在する API 名) にアクセスしたい場合
module Foo
class File
end
class Bar
def initialize
File # -> Foo::File を参照する
# 探索する順序は Foo::Bar::File (not found) -> Foo::File (found)
end
end
end
class Foo::Bar
def initialize
File # -> toplevel の File (= 組み込みライブラリの File) を参照する
# 探索する順序は Foo::Bar::File (not found) -> ::File (found)
# 名前空間を指定しても記述上入れ子になっていないので Foo::File は見に行かない
end
end
第9章 例外処理を理解する
Ruby におけるいわゆる try-catch は "begin-rescue"
code:ruby
begin
# ...
rescue <exceptions to catch (optional)> => e
# ...
end
Exception が最上位
その下がシステム関連の例外と StandardError
StandardError の下がよくあるやつ
Java 的に解釈すると ruby の Exception が Throwable 相当
rescue で例外を指定しなかった場合は StandardError 以下の例外だけが拾われる
他の特殊な例外とかを拾いたい時はそれも指定するか Exception を指定してガバッと取る
retry
rescue ブロック内で retry を呼ぶと begin ブロックをもう一度実行する
raise
引数
無し -> RuntimeError
string -> RuntimeError + 与えたメッセージ
exception, string -> 与えた exception + 与えたメッセージ
exception -> 与えた exception
exception class を渡してもインスタンスを作って渡しても OK
例題 (正規表現チェッカー) のテストを書く
メイン実装のメソッド化
ファイルを ruby command で直接実行された時にメソッドを呼び出すようにする
__FILE__ と $0 の比較
テストでは gets を stub して std in を偽装
assert_output
ensure
finally みたいなやつ
begine-rescue-else-ensure で全部
rescue は演算子として式の後に置いて例外を拾って値を返すみたいなことができる
foo = do_something(a, b) rescure 'error raised' みたいな
第10章 yield と Proc を理解する
メソッドに与えられた block は yield で呼び出す
block_given? で渡されているか確認できる
メソッドの引数に定義されてなくても block は渡せる
yield に引数を与えて block に与えることができる
過剰な分は無視され、足りない分は nil で渡される
yield は block の返却値を返す
Block 自体は引数として明示的に求めることもできる
& prefix をつける
一つのメソッドに一つだけ
最後の引数として定義する
Proc
4つの作成記法、2種類のインスタンス
普通の Proc インスタンス
code:ruby
# Proc の constructor で作成
Proc.new { |a, b| a + b }
# Kernel module の proc method
proc { |a, b| a + b }
呼び出し時の引数の過不足に対して寛容
ラムダ Proc インスタンス
code:ruby
# -> 構文で作成
-> (a, b) { a + b }
# Kernel module の lambda method
lambda { |a, b| a + b }
呼び出し時に引数の数が一致しないと ArgumentError を投げる
その他の性質の違い
proc.lambda? でラムダか否かを判定できる
メソッド引数における Proc の扱い
& つき
呼び出し時に Proc object を渡す場合呼び出し側でも & をつける
prefix 無し
普通の引数として渡す
Proc オブジェクトの呼び出し方
proc.call(a)
proc.yield(a)
proc.(a)
proc[a]
proc === [a]: case-when で使う
&, to_proc
& は Proc オブジェクトを block としてメソッドに認識させて渡す (&foo に対応させる) 機能も持つ
本来の機能としては to_proc 呼んで対象を Proc 化している
Symbol の Proc 化 (= メソッドの Proc 化) ができる
Proc 化した symbol は第一引数がレシーバ、第二引数以降がメソッド本来の引数になる
第11章 パターンマッチを理解する
case-in
どの条件ともマッチしなかった場合は例外が投げられる
^ (pin operator)
定義済みの変数を in の条件として参照したい場合に使える
^ で指定された箇所は === による比較になるので class のインスタンスだったりでもマッチする
指定できる変数はローカル変数のみ
deconstruct_keys について
code:ruby
hash:x = @x if keys.nil? || keys.include?(:x) keys が nil だったら要素を含めるの?
nil の場合は全てのメンバを意味します。
第12章 Ruby のデバッグ技法を身につける
第13章 Ruby に関するその他のトピック