CrystalにはNilという型があり、値はnilの1つだけです。
これは値が存在しないことを表すために使われ、他の言語のnullやNoneに似ています。
nilはさまざまなメソッドの戻り値として返ることがあります。たとえばString#[]?は、インデックスが範囲外のときにnilを返します。
"foo"[10]? # => nil
Crystalにはnull参照チェックと呼ばれる仕組みがあります。これは、すべての型がデフォルトでnilを許容しないという意味です。
つまり、変数を明示的にNil型として宣言しない限り、その変数にnilを代入することはできません。
そのため、コンパイラーがnull参照を自動的にチェックしてくれます。
nil値の扱い方には、いくつかの方法があります。
nilは偽とみなされる値で、falseも同じです。
つまり、偽とみなされる値や真とみなされる値が期待される文やその他の場所では、falseと同じように扱われます。
if nil
puts "nil is truthy"
else
puts "nil is falsey"
end
# => nil is falsey
つまり、nilになりうる変数の値がnilのときは、真とみなされる側の分岐は実行されません。
foo = "boo"[2]? # It is possible that foo is nil
if foo
p foo.class # => Char
end
nilかどうかを確認するCrystalには、値がnilかどうかを確認するためのメソッドがあります。
nil?といい、すべてのオブジェクトで利用できます。
foo = "boo"[2]?
if foo.nil?
puts "foo is nil"
else
puts "foo is not nil"
end
これは、if文でそのまま値を使う場合とは少し異なります。というのも、先ほどの例ではfalseとnilのどちらも偽として扱われていました。
ここではnilだけが偽として扱われます。値がfalseだったならnilではないので、真になっていたはずだからです。
nil値を扱うもっとも簡単な方法は、そもそもnilにならないようにすることです。
or演算子(||)はBoolsを扱うときによく使われますが、正しく理解すればnil値の扱いにも使えます。
or演算子は、最初の値が真かどうかを調べ、そうでなければ2番目の値を使います。
これを使えば、値がnilの場合は偽となるので、2番目の値が使われるようにできます。
"foo"[10]? || "bar" # => "bar"
陛下は、カルマル同盟の首都コペンハーゲンでディナーパーティーを開こうとしています。 一年で最大のパーティーで、すべての貴族が招待されています。 しかし、陛下を毒殺しようという陰謀があるという噂が、国中に広まっています。
そこで陛下は、毒見役であるあなたに、食べ物や飲み物に毒が盛られていないかを調べるよう命じました。 陛下が料理として出す予定なのはMushroom pastiesだけですが、飲み物はいろいろな種類があります。
陛下が注文したものと違う食べ物なら、毒が入っている可能性が高いです。 陛下の注文と違う食べ物は、捨てることになります。
check_food?メソッドを実装しましょう。このメソッドは、食べ物の名前をStringとして受け取るfoodという引数を取ります。
このメソッドは、Mushroom pastiesならその食べ物を返し、そうでなければnilを返します。
CastleDinner.check_food?("Mushroom pasties")
# => "Mushroom pasties"
CastleDinner.check_food?("Bread")
# => nil
飲み物に毒が入っている可能性もありますが、毒かどうかの見分けは少し難しいです。
名前にiという文字が含まれていない飲み物には毒が入っています。大文字か小文字かは問いません。
毒が入っている飲み物は、捨てることになります。
check_drink?メソッドを実装しましょう。このメソッドは、飲み物の名前をStringとして受け取るdrinkという引数を取ります。
このメソッドは、毒が入っていなければその飲み物を返し、そうでなければnilを返します。
CastleDinner.check_drink?("Apple juice")
# => "Apple juice"
CastleDinner.check_drink?("Tea")
# => nil
陛下は、招待した客に食べ物に毒が入っているのではないかと心配させたくはありません。 そのため、飲み物に毒が入っている場合は、別の飲み物、つまりApple juiceに差し替えることになります。
replace_drinkメソッドを実装しましょう。このメソッドは、飲み物の名前をStringとして受け取るdrinkという引数を取ります。
このメソッドは、毒が入っていなければその飲み物を返し、そうでなければ"Apple juice"を返します。
CastleDinner.replace_drink("Orange juice")
# => "Orange juice"
CastleDinner.replace_drink("Tea")
# => "Apple juice"