Ruby Silver 試験ポイントまとめ(自分用)
自分用メモで汚いですが間違えやすいところをまとめてますぜひ
2020/9/14 合格した。94点
-
模擬と完全に同じ問題は5問ぐらいあったかな
-
模擬試験やっておけば、ほとんど初見の問題はない。ルビー開発を10年やっていても、試験対策しないと落ちると思う
-
試験会場は早めに着いても、受け付けはない。時間になると名前が呼ばれる、このタイミングで受け付け
-
テストを終えると合否が画面に出でくる
-
模擬試験を繰り返せば受かりそう。教本は読まなくていけそう、模擬試験で難しい問題は出なかった。合格させたい試験に思われる。基本情報試験より楽だと思われる
-
試験費用が高すぎる。5000にしてくれ。ゴールドが10000なら分かる。
-
試験は30分でおわた。30分で16000だと思うとパチスロより吸い込まれる感がある
-
一度不合格になった人向けに、無料受験キャンペーンをやる時がある。
-
試験は終われば帰れる
-
arrayの問題多かった
-
新宿の会場は混んでいた。
-
50hぐらいかかった。
-
arrayをただ後ろから並び変える方法、1の文字コードがわからなかった。raiseの後らrescueでexitした場合、ensureが動くのか?
-
12問間違えてもいい。38問正解スルヒツヨウガアル
-
Ruby 2.0以降では、デフォルトのスクリプトエンコーディングはUTF-8
-
Hash#eachのブロックパラメータはArray。
-
Hash#size はhashの要素の数を返す
-
Enumerable#select selfからブロックの条件に該当する要素を集めて返す。
-
Enumerable#find selfからブロックの条件に該当する最初の要素を返す。
-
Array#delete selfから引数の要素を削除。削除した要素を返す。破壊的メソッド。
-
Obejct-numeric-integer-fixnum,bignum
-
split('d'),split(/;|:/) 区切って配列に入れる
-
split 引数なしはスペースで区切る
-
split("") 一文字ずつ区切る
-
-split(",", 2) 2は区切る数 ["1","2,3,4,5"]
-
例外処理について
- Rescue 節で省略するとstandard error とそのサブクラスを拾う
- Ensure節は必ず実行される
- raiseメソッドは引数を指定していなければ、RuntimeError例外
を発生
- elseもある。rescueが実行されないときに実行。resucueの下に書く。
-
extend
-
オーバライド不可演算子
- ..,&&
-
八進数表記
- 0から始まるもの
- Puts 090 などでエラー
-
演算子
- A && B ...AがtrueだとBがうごく
- A && B ...AがfalseだとBがうごカナイ
- A || B ...AがfalseだとBが動く
- A || B ...AがtrueだとBが動かない
- A&BまたはA|B ... AがtrueデモfalseデモBガ動く
-
time
- strftime("ここに入れる")
- %Y YYYY
- %m MM 月
- %d dd
- %H
- %M 分
- %S 秒
-
演算子の優先順位は*よりも**が高い
-
Ruby定数は大文字で始める
- 変更可能だが警告が出る
- << で追加するときは警告なし
-
Ruby変数名
- 1文字目はアルファベット小文字か_で始める必要がある。
- 2文字目以降はアルファベットもしくは数字を使用
-
可変長変数
- *a などで宣言
- 任意の数の引数が取れ、aに配列が格納される
-
Initializeメソッドは何回でも読み出され書き直される
-
Super の記述場所は任意の位置でおk。同名のメソッドをオーバライドしている場合はスーパーのほうが呼ばれる
-
外部モジュール読み込みについて
- 数学PIなどをしようする場合Mathモジュールを読みこむ必要がある
- 読み込み方法例
- Include Math
- Math::PI
-
クラス拡張のメソッドより特異メソッドが優先して実行される
-
.. , … 演算子について
- a = [1,2,3,4]
- a[0..3] => [1,2,3,4]
- a[0..-1] => [1,2,3,4]
- a[0,3] => [1,2,3]
-
Timeオブジェクト、datetimeオブジェクト管理する日付時刻をフォーマットするにはsrtftime
-
Timeオブジェクト同士の減算は秒になる
-
File.join('/',"aaa","bbb") -> /aaa/bbb /は/で結合する意味ではない。
-
File.join("aaa","bbb") -> aaa/bbb 通常はこっちかな
-
find_all == select
-
map == collect
-
find == detect
-
updade! == merge
-
delete_if = reject! ブロックが真になるあたいを取り除く
-
%w(a b c) -> ["a","b","c"]
-
"abcde".index("c") -> 2
-
"abcdec".index("c",2) -> 2 (indexの位置から探す
-
"abcdec".index("c",3) -> 5
-
{"foo" => 1}
-
{:foo -> 1}
-
{ foo : 1}
-
hash[:foo,1]
-
ポイントハ=はNG,"foo":1モNG、右は文字なら""必要
-
正規表現記号
- *は直前の文字列の0回以上の繰り返しを表す
-
- は1回以上
- iオプションで大文字と小文字の区別しない
- . は任意の文字を表す
- {m}は直前の正規表現のm回の繰り返しを意味
- {m,} 直前最低m回繰り返し
- {m.n} 直前最低m回繰り返し 最高n回繰り返し
- Mオプションで.が改行にもマッチする
- \d+などの+は連続を表す
- Matchは一度しか行わない
- Scanは繰り返し行われる
- \wは単語構成文字の省略記号
-
65を”A”にしたい時
- 65.chr
- 逆は”A”.ord
-
for文、スコープは作られないので、内外の変数にアクセス可能
-
each文、スコープが作られる。外からeach内部の変数にアクセスするとエラーになる。each内部から外の変数は見える
-
'abcdefg'['bc'] -> bc
-
'abcdefg'.slice('bc') -> bc
-
'abcdefg'['bd'] -> nil
-
'abcdefg'.slice!('bc') -> bc, 中身も変わる'adefg'
-
'abcdefg'[/bc/] -> bc
-
'abcdefg'.slice(/bc/) -> bc
-
'abcdefg'.slice!(/bc/) -> bc, 中身アも変わる'adefg'
-
sub ... 非破壊、一つ置換 !がつけば破壊
-
gsub ... 非破壊、全部置換 !がつけば破壊
-
a='aabbccddeeffgg',
- a.delete('be') -> 消した結果を返すaaccddffgg。 変数aは変わらない 非破壊
- a.delete('a-f') 範囲指定可能
- a.delete('a-c','c-g') 複数条件時は両方に共通な文字cが消える
-
a='abcd'
- a.replace('xyz') => xyz,a='xyz' になる
-
連結 + << concat *
-
大文字小文字変換、!がつけば破壊
- downcase,upcase,swapcaseは反転
-
chomp ... \r \r\n \n を削除、非破壊
- 複数ついてる場合は1種類消える
- chopで消える場合は\も一緒に消える
- str = "foobarbaz\r\n\r\n"
- p str.chomp('').chop.chop.reverse
- 空文字指定は全部の改行コード消える。“braboof”
- "\r\n".chop -> kieru
- "\r".chop -> kieeru
- "\n".chop -> kieru
- "\n\n".cho- -> \n
-
reverse,reverse!
-
"abc\ndef\nghi".each{|c| puts c}
- デフォルトで改行毎にブロックにわたる。、オプション変更可能
- each_line,lineメソッドも同じ
-
a=1,2,3 -> [1,2,3]
-
array=[1,2,3] 参照、中身は変わらない
- array[0..1] -> [1,2]
- array[1] -> 2
- array.at(1) -> 2
- array.valuea_at(1) -> 2
- array.first -> 1
- array.first(3) => [1,2,3] 3ツ
- array.include?(3) -> true
- array.index(2) -> 1, これは値を探して、場所を返す(string.index(?)は?文字を探してindexを返す)
-
a=[1,2,3,4,5] 削除 中身が変わる
- array.delete_at(2) -> 3, a=[1,2,3,5] 添え字の値を削除、破壊(string.deleteは消した結果を返す、hi破壊メソッド)
- array.detete_if{|n| n % 2 ==0} -> [1,3,5] ,a=[1,3,5]
- reject!と同じ
- array.delete(3) -> 3 , a=[1,2,4,5] 値を削除
- array.slice!(2,2) ->[3,4] ,a=[1,2,5]
- array.shift(2) -> [1,2] , a=[3,4,5]
- array.shift -> 1, a=[2,3,4,5]
- popはシフトの逆
- -でも削除可能 [1,2]-[1]=>[2]
- []|[] 和集合
- []&[] 重複
-
配列の繰り返し
- each 各要素がブロックへ
- each_index 配列のindexがブロックへ
-
[1,2,3].join(",") -> "1,2,3"
-
array=[1,2,3]
- a[10]=10,トスルト a[4-9]ハnilガハイル
-
[].uniq -> 重複削除、非破壊、!がつけば破壊
-
[].compact -> nil 削除、非破壊、!がつけば破壊
-
[1,2,3,4,5].map{|n| n*2} -> [2,4,6,8,10]
-
[1,2]*4 -> [1,2,1,2,1,2,1,2]
-
ハッシュの作り方
- {"foo" => 1} 値が文字列なら""を付ける.文字列キーにするにはこの方法しかな
- {10 =>1}も可能、a[10]->1トナル
- Hash["foo", 1] => {"foo" => 1}
- {"foo" => 1,} # {"foo"=>1}
- {:foo => 1,} # {:foo=>1}
- {foo: 1,} # {:foo=>1}
- {10=> 1,} # {10=>1}
- Hash["foo", 1,] # {"foo"=>1}
- Hash[:foo, 1,] # {:foo=>1}
- Hash[foo: 1,] # {:foo=>1}
- Hash[10, 1,] # {10=>1}
- 注意 = はNG,"foo":1もない
- a["hoo"] -> 1
- a[:foo] -> 1
-
ハッシュメソッド
- hash={"apple"=>"fruit","coffee"=>"drink"}
- hash.keys -> ["apple", "coffee"] 配列で
- hash.value -> ["fru","dri"]
- hash={1 => "a", 3=>"b"}
- hash.value_at(1,3) -> ["a","b"] keyに対応する値を配列で返す
- hash.fetech("1")-> a
- a={1 =>"a",2 =>"b",3 =>"c",4 =>"d"}
- hash.select(|key,value| key % 2==0} -> [[2,b],[4,d]] 結果が真となる配列の組み合わせを返す
- hash={"apple"=>"fruit","coffee"=>"drink"}
-
ハッシュ変更
- hash={"apple"=>"fruit","coffee"=>"drink"}
- hash["apple"]="red" -> "red", hash={"apple"=>"red","coffee"=>"drink"}
- hash["orange"]="orange" -> orange ,hash={"apple"=>"red","coffee"=>"drink","oragen"=>"orange"} 追加される
- hash.delete("apple") -> "fruite" ,hash={"coffee"=>"drink"}
- hash.reject{|key,value| value == "drink"} ->{"apple"=>"fruit"}, 真に値を取り除いて返す。hashは変わらない
- hash.reject!{|key,value| value == "drink"} ->{"apple"=>"fruit"}, 真に値を取り除いて返す。hashは変わる。hash={"apple"=>"fruit"}, delete_ifと同じ機能
- replaceは引数の中身に代わる。a=変わる
- hash.merge は引数のハッシュとマージ、新規キーは追加、既存のキーはバリューを上書き、マージした結果を返す、非破壊
- hash.merge! == updateは同じ機能。、マージした結果を返す、破壊メソッド
- has_key?,inclued?,key?,member? ハッシュにkeyが存在すればtrueを返す
- has_value?,value? ハッシュにvalueが存在すればtrueを返す
- each,each_pair for文である。keyとvalueをブロックへ
- each_key keyを
- each_value valueを
- hash.sort keyで昇順ソート 配列で返す
- hash={"apple"=>"fruit","coffee"=>"drink"}
-
File.delete('readme'
-
FIle.rename('before','after') afterになる
-
IO.read('readme',5) 先頭から5バイト出力、未指定時はfile全部読み込んで、変数にstringで全部入る
-
IO.foreach('readme'7){ |line| puts line} 1行ずつ読み込む
-
open('readme').readlines ファイル全部読み込んで行ごとに配列へ
-
io.gets 1行読み込み
-
io.readline 1行読み込み
-
IO出力ハwrite puts print
-
lineno => getsメソッドが呼び出された回数
-
rewind , ファイルポインタが先頭へ、lineno=0
-
pos=? ファイルポインタ移動
-
io.seek(10) = io.seek(10,IO::SEEK_SET) ポインタを先頭から10バイト移動
-
io.seek(10,IO::SEEK_CUR) 現在位置から移動
-
io.seek(10,IO::SEEK_END) ファイル末尾から移動
-
Time.now,Time.new 現在時刻のtimeオブジェクトを返す
-
[1,2,3,4,5].inject(0){|result,v| result + v ** 2} -> 55タシザンシタケッカ
-
each_with_index{|item,i| i=0カラ
-
(1..10).each_cons(3) ->
- [1,2,3]
- [2,3,4]
- ...
- [8,9,10]
-
(1..10).each_slice(3) ->
- [1,2,3]
- [4,5,6]
- ...
- [10]
-
[1,2,3,4,5].include?(3)-> true
-
find == detect
- [1,2,3,4,5].find{|i| i % 2 == 0} -> 2 真ヲ1イッコ
-
[1,2,3,4,5].seletc{|i| i % 2 == 0} ->[2,4]
-
["aaa","b","cc"].sort{|a,b| a.length <==> b.length} -> [ "b","cc","aaa"]
-
ヒアドキュメント
- <<-識別子' のように-' を付けて書くことで終端行をインデントすることは可能
- 通常は空白を開けるとエラー
-
Chompメソッドで\r\n 1つ消す
-
chopメソッドで末尾の文字を消す
-
IO#rewindはファイルポインターを先頭に移動する
-
Splitメソッドは引数で特定の文字列を区切り文字として指定できる。また第二引数で生成される配列の個数を指定することができる。
-
“0-5”は範囲指定になるが、”8-”では範囲指定にならず8と-を削除します
-
eql は 文字列が同じ場合true
-
equal は オブジェクトが同じかどうかを見る
-
inject はブロックに要素を割り当てる…|i,j|
-
IO
- IO#getsとIO#readlineはファイルオブジェクトから一行読み込んで、読み込みに成功した時にはその文字列を返す。
- IO#getsとIO#readlineの違いはEOFに到達した時の振る舞いのみ。IO#getsはnil, IO#readlineはEOFErrorを返す
foo = [1,2,3]
bar = foo
p foo === bar # => true // equalと一緒なのかな?
Module Foo
def foo //インスタンスメソッドとして定義
puts("foo")
end
end
Class Bar
extend Foo //FooモジュールのインスタンスメソッドをBarクラスの特異メソッドとして定義
end
Bar.foo #=> foo という使い方ができる
-
::Fooのような::演算子から始まる定数は、トップレベルで定義されている定数
-
slice!はスライス後を参照している
-
ファイルの末尾(EOF)への反応の仕方
- getsの場合、nilが帰る
- Readlineの場合、EOFError例外が発生する
-
File#mtimeメソッドは更新が保存されるがFile::Stat#mtimeメソッドは更新が保存されない
- File::Statクラスはnewメソッドのみクラスメソッド
-
ファイル名と拡張子が".“で区切られているファイル名から任意の拡張子を取り除く時に、第2引数として”.*"を指定してFile.basenameを呼び出します
-
"%2d%s"の"%2d"は出力したい数値が2桁より少なければ、空白スペースを入れるようにする指定
(1..5).each_cons(3) {|arr| p arr }
# <実行結果>
# [1, 2, 3]
# [2, 3, 4]
# [3, 4, 5]
(1..10).each_slice(3) {|arr| p arr }
# <実行結果>
# [1, 2, 3]
# [4, 5, 6]
# [7, 8, 9]
# [10]
arr = [1,2].zip([3,4])
p arr
# <実行結果>
[[1, 3], [2, 4]]
a1 = [1,2,3]
a2 = [4,2,3]
p a1 - a2
# <実行結果>
[1]
p ({a: 100, b: 100}).invert
# <実行結果>
# {100 => :b}
1: s = ["one", "two", "three"]
2: s.shift
3: s.shift
4: s.unshift
5: s.push "four"
6: p s
# <実行結果>
# 1: ["one", "two", "three"]
# 2: ["two", "three"]
# 3: ["three"]
# 4: ["three"]
# 5: ["three", "four"]
p "Ruby on Rails".delete("Rails")
# <実行結果>
# "uby on "
a, b = 0 # a => 0, b => nil
c, a = 1 # c => 1, a => nil
a, d = 1, 2 # a => 1, d => 2
b, c = 3 # b => 3, c => nil
selfがわからんからこれ覚える
class Blog
def foo # インスタンスメソッド
end
def self.foo # クラスメソッド(このselfはクラス自身(Blog)を指す)
end
def bar # インスタンスメソッド
self.foo # インスタンスメソッドのfooが呼ばれる(このselfはインスタンス自身(Blog.new)を指す)
foo # インスタンスメソッドのfooが呼ばれる
self.class.foo # クラスメソッドのfooが呼ばれる(このような記述でインスタンスメソッド内でもクラス自身を指すことができる)
end
def self.bar # クラスメソッド(このselfはクラス自身を指す)
self.foo #クラスメソッドのfooが呼ばれる
foo #クラスメソッドのfooが呼ばれる
end
end
self="My job is a sales"である
class String
def change_job
self.replace("I will become an engineer!")
end
end
s = "My job is a sales"
s.change_job
p s
selfは4
class Interger
def aaaa
return self
end
end
print 4.aaaa
一文字マッチするものがマッチ
a = ['3p', '913', 'Zx', 'ssr', 'M7', 'W']
a.grep(/[A-Z0-9]/)
["3p", "913", "Zx", "M7", "W"]
keyのソートsort1、非破壊
h = { "def" => 2, "ghi" => 1, "abc" => 3 }
p h.sort
[["abc", 3], ["def", 2], ["ghi", 1]]
h = { "def" => 2, "ghi" => 1, "abc" => 3 }
p h.sort.reverse
[["ghi", 1], ["def", 2], ["abc", 3]]
vauleのsort、非破壊
昇順
h = { "def" => 2, "ghi" => 1, "abc" => 3 }
p h.sort{ | a, b | a[1] <=> b[1] }
p h
[["ghi", 1], ["def", 2], ["abc", 3]]
降順
h = { "def" => 2, "ghi" => 1, "abc" => 3, "ddd" => 5 }
p h.sort{ | a, b | b[1] <=> a[1] }
[["ddd", 5], ["abc", 3], ["def", 2], ["ghi", 1]]
a,bに-がつくパターンもある意味が分からん
include で読み込めるのは?インスタンスメソッド、インスタンス変数、定数の情報を定義しているクラスに追加します。
module Foo
Bar = "bar"
end
class Baz
include Foo
end
puts Baz::Bar #=> bar // 定数だからなぜか定数はこれでいける。メソッドはnewシナイトダメ
extend で読み込めるのは?のインスタンスメソッドを特異メソッドとしてクラスに追加します。クラスメソッド扱い
module Foo
def foo
puts("foo")
end
end
class Bar
extend Foo
end
Bar.foo #=> foo
a = "foo"
b = a
b.slice!(0, 1) ->fが出る。!ナノデooになる
print(a, b) -> oooo
- マシンのポインタのサイズは31ビット幅とします。31bitならbignumになる
- Fooクラスの定義の中でattr_accessorメソッドを呼び出すと、引数に指定された名前のインスタンス変数を作成し、そのインスタンス変数に対する書き込みと読み込みのアクセサメソッドをFooクラスに定義します
- attr_accessor :foo ->インスタンス変数
- とすれば
- bar = Bar.new(“bar”)
- puts(bar.foo)
Dunce\スペースは改行されない
File.open("hats.txt", "w") do |f|
f.puts(%w[Bowler Deerstalker Dunce\ cap Fedora Fez])
end
hats.txt
Bowler
Deerstalker
Dunce\ cap
Fedora
Fez
File.statはopenしたときのスナップショット
file = File.open("hello.rb", "w")
stat = file.stat
mtime1 = stat.mtime //onaji
file.puts("new data")
file.flush
mtime2 = stat.mtime // onaji
カレント、親
["tmp", "tmp/lang", "tmp/lang/ruby", "tmp/lang/python"].each do |dir|
Dir.mkdir(dir)
end
Dir.chdir("tmp/lang")
Dir.new(".").each do |entry| // . , .. , ruby, python の4ディレクトリ
filename = File.join(entry, "rocking.rb")
File.open(filename, "w")
end
Dir.rmdir("python")
- File.basename(filename, “.*”) // ファイル名から拡張子をとる
- File.extname("child/kenji.rb") // .rb
- 書き込み
- puts 改行あり
- print なし
- write ナシ
File.open("fancy.txt", "w") do |f|
f.write("R u b y\n")
f.puts(["u","b","y"]) // 配列1ツズツ改行
end
puts File.read("fancy.txt")
R u b y
u
b
y
File.splitはディレクトリ部分とファイル部分に文字列オブジェクトの引数を分割し、両方から末尾のスラッシュを取り除きます。2つの部分は2つの要素を持つ1つの配列として返されます。問題のプログラムでは、配列の[“/home/john”, “bookmarks”]が返されます。
joinメソッドはFile.splitの返した値である配列で呼ばれています。つまりArray#joinが呼び出され、File.joinではありません。Array#joinは配列の要素に文字列を単純に連結するので、(d)が正解です。
p File.split("/home/john/bookmarks/").join
“/home/johnbookmarks”
-
File.joinと間違えるな
- [“/home/john”, “bookmarks”].join -> /home/johnbookmarks
- [“/home/john”, “bookmarks”].join("_") -> /home/\
-
odd? 奇数か?
-
even? 偶数か?
-
ブロックには|a,0| |b,1| |c,2| ....
-
breakメソッドの引数が指定されていれば、その値がブロック付きメソッド呼出しの返す値になります。インデックスは0から数え始め、最初に奇数になるインデックスは1です。each_with_indexメソッドはインデックスが1の値である"b"を返して繰り返しを終えます。そして、返された"b"はローカル変数のfooに代入されます。
foo = ('a'..'z').each_with_index {|i, n| break(i) if n.odd? }
p foo.succ
-
0 % 3 ... あまり0
-
3 % 3 ... あまり0
-
6 % 5 ... あまり1
-
array のソート
- 1次元
- array.sort 昇順
- array.sort.reverse
- 2次元
- self <=> other」メソッドの場合はselfとotherを比較して、selfが大きい時に正、等しい時に0、小さい時に負の整数を返します→ダカラ何ッテカンジ意味わからん
- 1次元
score = [["kobayashi", 86],["murata", 54],["azuma", 72]]
puts "a-shoujun"
p score.sort { |a, b| a[0] <=> b[0] }
p score.sort { |a, b| -a[0] <=> -b[0] } # 数値をマイナスをつけているが意味がわからん
puts "a-koujun"
p score.sort { |a, b| a[0] <=> b[0] }.reverse
p score.sort { |a, b| b[0] <=> a[0] }
p score.sort { |a, b| -b[0] <=> -a[0] } # musi
puts "b-shoujun"
p score.sort { |a, b| a[1] <=> b[1] }
p score.sort { |a, b| -b[1] <=> -a[1] } # musi
puts "b-koujun"
p score.sort { |a, b| a[1] <=> b[1] }.reverse
p score.sort { |a, b| b[1] <=> a[1] }
p score.sort { |a, b| -a[1] <=> -b[1] } # オボエテオコウ
a-shoujun
[["azuma", 72], ["kobayashi", 86], ["murata", 54]]
[["azuma", 72], ["kobayashi", 86], ["murata", 54]]
a-koujun
[["murata", 54], ["kobayashi", 86], ["azuma", 72]]
[["murata", 54], ["kobayashi", 86], ["azuma", 72]]
[["murata", 54], ["kobayashi", 86], ["azuma", 72]]
b-shoujun
[["murata", 54], ["azuma", 72], ["kobayashi", 86]]
[["murata", 54], ["azuma", 72], ["kobayashi", 86]]
b-koujun
[["kobayashi", 86], ["azuma", 72], ["murata", 54]]
[["kobayashi", 86], ["azuma", 72], ["murata", 54]]
[["kobayashi", 86], ["azuma", 72], ["murata", 54]]
スラッシュハ増えないようだ。 /// にはならないひっかけ
https="https://"
domain="example.jp"
dir="index.html"
puts File.join(https, domain, dir)
https://example.jp/index.html
# https:/ -> ソノママ
# https: -> https:/
true & a = "We" # 左辺に関係なく、右辺実行
puts a # We
false | b = "are" # 左辺に関係なく、右辺実行
puts b # are
2 == 1 && c = "Ruby" # 左辺がfalseナノデ、オワル
puts "c=nil" if c.nil? #nil
5 > 3 || d = "Engineer" #左辺がtrueナノデオワル
puts "d=nil" if d.nil? # nil
p = a + b + c + d # we + are + nil でエラーになります
h = {:name => 'sato', :club => 'tennis'}.fetch(:name, 'error')
print h # sato
h = {:name => 'sato', :club => 'tennis'}.fetch(:nam, 'error')
print h # error