概要
- Ruby技術者認定試験のSilver/Gold対策の勉強で、私自身が何回も間違ってしまう問題を集めてみました
- 掲載した問題と解説は、基本、RubyExaminationを参考にしていますので実際に対策に取り組む場合は、そちらのサイトで取り組んでください!
個人的なハマり問題集
問題1. Enumerable#injectの使い方
# 次のコードを実行するとどうなりますか
p [1, 2, 3].inject{|x, y| x + y ** 2} rescue p $!
#=> 14
p [1, 2, 3].inject(0){|x, y| x + y ** 2} rescue p $!
#=> 14
p [1, 2, 3].inject([]){|x, y| x << y ** 2} rescue p $!
#=> [1, 4, 9]
p [1, 2, 3].inject do|x, y| x + y ** 2 end rescue p $!
#=> #<LocalJumpError: no block given>
解説
- Enumerable#injectはブロックを使用して繰り返し計算を行うメソッド
- injectメソッドには以下のような特徴がある
- ブロック引数の1番目は前回の戻り値が渡される。初回は、初期値が渡される
- ブロック引数の2番目は要素が順番に渡される
- 引数を省略した場合は、要素1が初期値になる
- 引数を指定した場合は、その値が初期値になる
- 4行目は、[1, 2, 3].injectまでがpメソッドの引数となるため、pメソッドへブロックが不正に渡されるため、エラーとなる
p([1, 2, 3].inject) do|x, y|
x + y ** 2
end rescue p $!
do...end
と{...}
挙動の違いに関しては、以下の記事でも取り上げている
ブロックをdo…endで書くか{…}で書くかにより挙動が変わる例
参考
- https://docs.ruby-lang.org/ja/2.1.0/method/Enumerable/i/inject.html
- Ruby の inject メソッドを使いこなす
- ブロックをdo…endで書くか{…}で書くかにより挙動が変わる例
問題2. do ... end
と{ ... }
の挙動の違い
# 次のコードを実行するとどうなりますか
p [1,2,3,4].map do |e| e * e end
#=> #<Enumerator: [1, 2, 3, 4]:map>
解説
-
do ... end
の結合度が弱いため、p([1, 2, 3, 4].map)とpが評価されてしまう - 問題のように式の内容を直接使用する際は、{ ... }を使用
-
do...end
と{...}
挙動の違いに関しては、以下の記事でも取り上げている
# {…}だと、思った通りの出力になる
p [1,2,3,4].map { |e| e * e }
#=> [1, 4, 9, 16]と出力される
参考
問題3. Stringクラスの似たメソッド
# 次のコードを実行するとどうなりますか
p " Liberty Fish \r\n".strip
#=> "Liberty Fish"
p " Liberty Fish \r\n".chomp
#=> " Liberty Fish "
p " Liberty Fish \r\n".chop
#=> " Liberty Fish "
p " Liberty Fish ".chop
#=> " Liberty Fish "
解説
Method | 説明 |
---|---|
String#strip(String) | 文字列の先頭と末尾の空白文字(\t\r\n\f\v)を取り除きます。 |
String#chomp(String) | 末尾から改行コードを取り除きます。 |
String#chop(String) | 末尾の文字を取り除きます。ただし、文字列の末尾が"\r\n"であれば、2文字とも取り除きます。 |
参考
- https://docs.ruby-lang.org/ja/2.1.0/method/String/i/strip.html
- https://docs.ruby-lang.org/ja/2.1.0/method/String/i/chomp.html
- https://docs.ruby-lang.org/ja/2.1.0/method/String/i/chop.html
問題4. Enumerable#each_consとEnumerable#each_slice
# 次のコードを実行するとどうなりますか
(1..10).each_cons(3) {|arr| p arr }
#=> [1, 2, 3]
#=> [2, 3, 4]
#=> [3, 4, 5]
#=> [4, 5, 6]
#=> [5, 6, 7]
#=> [6, 7, 8]
#=> [7, 8, 9]
#=> [8, 9, 10]
(1..10).each_slice(3) {|arr| p arr }
#=> [1, 2, 3]
#=> [4, 5, 6]
#=> [7, 8, 9]
#=> [10]
解説
メソッド | 説明 |
---|---|
Enumerable#each_cons | enum.each_cons(n){ |arr| block } 引数nで指定した数の要素を繰り返し取り出して、ブロックを実行します。 ブロック引数arrには、取り出した要素が配列で入ります。 nの数が要素数より大きいときは、ブロックを1度も実行しません。 [要素1, 要素2, 要素3, ...]、[要素2, 要素3, 要素4, ...]、[要素3, 要素4, 要素5, ...]、...のように取り出します。 |
Enumerable#each_slice | enum.each_slice(n){ |arr| block } 引数nで指定した数の要素を繰り返し取り出して、ブロックを実行します。 ブロック引数arrには、取り出した要素が配列で入ります。 [要素1, 要素2, 要素3]、[要素4, 要素5, 要素6]、[要素7, 要素8, 要素9]のように取り出します。 |
参考
- https://docs.ruby-lang.org/ja/2.1.0/method/Enumerable/i/each_cons.html
- https://docs.ruby-lang.org/ja/2.1.0/method/Enumerable/i/each_slice.html
問題5. Array#zipとArray#transpose
# 次のコードを実行するとどうなりますか
p [1, 2].zip([3, 4])
#=> [[1, 3], [2, 4]]
p [[1, 2], [3, 4]].transpose
#=> [[1, 3], [2, 4]]
p [1, 2, 3].zip([3, 4])
#=> [[1, 3], [2, 4], [3, nil]]
p [[1, 2, 3], [3, 4]].transpose
#=> `transpose': element size differs (2 should be 3) (IndexError)
解説
- zipとtransposeの大きな違いは、配列内の各配列の要素数が一致しないときの挙動である点に注意
- zip: 足りない要素はnilで埋められ、余分な要素は捨てられる
- transpose: 例外IndexErrorが発生する
メソッド | 説明 |
---|---|
Array#zip | array.zip(other_array, ...) 配列の要素を引数の配列other_arrayの要素と組み合わせ、配列の配列を作成して返します。transposeメソッドで[array, other_array, ...].transposeとしたときと同じく、行と列を入れ替えます。ただし、transposeメソッドと違って足りない要素はnilで埋められ、余分な要素は捨てられます。 |
Array#transpose | array.transpose 配列の配列を行と列からなるデータと見立てて、行と列を入れ替えた配列の配列を作成して返します。配列内の各配列の要素数が一致しないときは例外IndexErrorが発生します。 |
参考
- https://docs.ruby-lang.org/ja/2.1.0/method/Enumerable/i/zip.html
- https://docs.ruby-lang.org/ja/2.1.0/method/Array/i/transpose.html
問題6. Hashクラスのメソッド
# 次のコードを実行するとどうなりますか
hash = { 'apple' => 'grate', 'banana' => 'ole', 'orange' => 'juice' }
p hash.member?('apple')
#=> true
hash = { 'apple' => 'grate', 'banana' => 'ole', 'orange' => 'juice' }
p hash.to_a
#=> [["apple", "grate"], ["banana", "ole"], ["orange", "juice"]]
hash = { 'apple' => 'grate', 'banana' => 'ole', 'orange' => 'juice' }
# updateは、破壊的メソッドである点に注意!
hash.update('grape' => 'juice')
p hash
#=> {"apple"=>"grate", "banana"=>"ole", "orange"=>"juice", "grape"=>"juice"}
hash = { 'apple' => 'grate', 'banana' => 'ole', 'orange' => 'juice' }
# clearは、破壊的メソッドである点に注意!
p hash.clear
#=> {}
解説
- updateメソッドは、
破壊的メソッド
である点に注意
メソッド | 説明 |
---|---|
Hash#include?, member? | hash.member?(key) ハッシュが引数keyと同じキーを持っていればtrue、なければfalseを返します。has_key?, key?, include?の別名です。 |
Hash#to_a | hash.to_a ハッシュを配列に変換して返します。[キー, 値]を並べた配列の配列ができます。 |
Hash#update | hash.update(other_hash) merge!の別名です。レシーバhashの内容に引数other_hashの内容を加えます。 |
Hash#clear | hash.clear キーと値をすべて削除し、ハッシュを空にします。レシーバ自身を変更するメソッドです。戻り値はレシーバ自身です。 |
参考
- https://docs.ruby-lang.org/ja/2.1.0/method/Hash/i/update.html
- https://docs.ruby-lang.org/ja/2.1.0/method/Hash/i/to_a.html
- https://docs.ruby-lang.org/ja/2.1.0/method/Hash/i/include=3f.html
問題7. メソッド内の定数
# 以下のコードを実行するとどうなりますか
def hoge
x = 10
Y = x < 10 ? "C" : "D"
puts Y
end
hoge
#=> Error!! dynamic constant assignment
解説
Rubyではメソッド内で定数を定義することができない
参考
アルファベット大文字 ([A-Z]) で始まる識別子は定数です。
定数の定義 (と初期化) は代入によって行われますが、メソッドの中では定義できません。
一度定義された定数に再び代入を行おうと すると警告メッセージが出ます。
定義されていない定数にアクセスすると例外 NameError が発生します。
問題8. 文字列の取り出し
# 次のコードを実行するとどうなりますか
str = 'abcdefghijk'
p str[2, 4]
#=> "cdef"
p str.slice(2, 4)
#=> "cdef"
p str[2..4]
#=> "cde"
p str.slice(2..4)
#=> "cde"
p str[2...4]
#=> "cde"
p str.slice(2...4)
#=> "cde"
解説
- []とsliceメソッドは、同じメソッド
メソッド | 説明 |
---|---|
str[idx] | その位置の文字を整数のコードで返します(0が1番目、1が2番目、...)。範囲外の位置を指定すると、nilが返ります。 |
str[idx, len] | idxの位置からlen文字の文字列を返します(位置は0が1番目、1が2番目、...です)。範囲外の位置を指定すると、nilが返ります。 |
str[range] | 範囲に対応する部分文字列を返します。範囲外の位置を指定すると、nilが返ります。 |
参考
問題9. Date.strftimeの引き数
# 次のコードを実行するとどうなりますか
require 'date'
p Date.today.strftime('%F')
#=> "2018-09-13"
p Date.today.strftime('%Y-%m-%d')
#=> "2018-09-13"
p Date.today.to_s
#=> "2018-09-13"
p Date.today.strftime('%x')
#=> "09/13/18"
p Date.today.strftime('%m/%d/%y')
#=> "09/13/18"
p Date.today.strftime('%D')
#=> "09/13/18"
解説
-
%F
,%Y-%m-%d
,to_s
は等価- 出力例: "2018-09-13"
-
%x
,%D
,%m/%d/%y
は等価- 出力例: "09/13/18"
フォーマット | 説明 |
---|---|
%F | %Y-%m-%d と同等 (ISO 8601の日付フォーマット) |
%Y | 西暦を表す数 |
%y | 西暦の下2桁(00-99) |
%x | %m/%d/%y と同等 |
%m | 月を表す数字(01-12) |
%d | 日(01-31) |
%D | %m/%d/%y と同等 |
参考
問題10. ==, eql?, equal?の違い
x = 1
y = 1.0
p x == y
#=> true
p x.eql? y
#=> false
p x.equal? y
#=> false
p x.equal?(1)
#=> true
解説
- Rubyでオブジェクトの比較をするメソッドは大きく分けて
==
、eql?
、equal?
の3つがある
メソッド | 説明 |
---|---|
== | 数値として等しいかを判定します。1と1.0は同じ数値として判定され、trueになります。 |
eql? | 同じクラスのオブジェクトかつレシーバーにある==メソッドで等しいと判定された場合にtrueになります。 |
equal? | オブジェクトIDが同じであれば、trueになります。 |
- Rubyリファレンスによると
eql?
は非推奨らしい。-
eql?メソッドを使わずに、「文字列の内容が同じかどうか」を調べるには==メソッドを、「同じオブジェクトかどうか」を調べるにはequal?メソッドを使ってください。
と記載されている - https://ref.xaio.jp/ruby/classes/string/eql
-
参考
- https://ref.xaio.jp/ruby/classes/string/eql
- Rubyにおける==と===とeql?とequal?の違いを今更
- Rubyの == と equal? と === と eql? のまとめ
問題11. Lazyとchunkの使い方
Lazy,chunk
# 次のコードを実行するとどうなりますか?
p (1..100).each.lazy.chunk(&:even?)
#=> #<Enumerator::Lazy: #<Enumerator: #<Enumerator::Generator:0x00007fd3711d8b38>:each>>
最初の5つの値
# 先頭から5つの値を取り出すにはどのメソッドが必要ですか?
# 答え:first(5), take(5).force
p (1..100).each.lazy.chunk(&:even?).first(5)
#=> [[false, [1]], [true, [2]], [false, [3]], [true, [4]], [false, [5]]]
p (1..100).each.lazy.chunk(&:even?).take(5)
#=> #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator: #<Enumerator::Generator:0x00007f920e1d8a88>:each>>:take(5)>
p (1..100).each.lazy.chunk(&:even?).take(5).force
#=> [[false, [1]], [true, [2]], [false, [3]], [true, [4]], [false, [5]]]
解説
- Lazyは、map や select などのメソッドの遅延評価版を提供するためのクラス
- chunkは、要素を前から順にブロックで評価し、その結果によって 要素をチャンクに分けた(グループ化した)要素を持つ Enumerator を返す
- Enumerator::Lazyは、とりあえずEnumeratorの用意はするが、数字を出すのは必要になってからになるので、非常に大きなRangeだとしても、すぐに値が返ってくる
- RubyのEnumeratorとEnumerator::怠惰(Lazy)の使い所とベンチマークの記事がわかりやすかった
メソッド | 説明 |
---|---|
Enumerator#Lazy | map や select などのメソッドの遅延評価版を提供するためのクラス |
Enumerator#chunk | enum.chunk {|item| block } 要素を前から順にブロックで評価し、その結果によって 要素をチャンクに分けた(グループ化した)要素を持つ Enumerator を返す |
Lazyが有効な例
# Lazyを使うとすぐに実行結果が返ってくるので、Lazyがとても有効
p (1..Float::INFINITY).each.lazy.select(&:even?).first(5)
#=> [2, 4, 6, 8, 10]
# Lazyを使わないと、Rangeの全ての奇数をselectしてから、first(5)が実行されるので
# 実行結果が返ってこない。
p (1..Float::INFINITY).each.select(&:even?).first(5)
- chunkは、`行単位データの並びに意味があるときに、その順位を崩さずに部分まとめ(チャンク)を生成するときはchunkを使うってことらしい
参考
- https://docs.ruby-lang.org/ja/2.1.0/class/Enumerator=3a=3aLazy.html
- https://docs.ruby-lang.org/ja/2.1.0/method/Enumerable/i/chunk.html
- RubyのEnumeratorとEnumerator::怠惰(Lazy)の使い所とベンチマーク
- 行指向ドキュメント処理で活躍するRubyのだんご化3兄弟といったら...
問題12. lambdaとprocの違い
lambda
# 次のコードを実行するとどうなりますか
# 答え:エラー!!
local = 0
p1 = lambda { |arg1, arg2|
arg1, arg2 = arg1.to_i, arg2.to_i
local += [arg1, arg2].max
}
p1.call("1", "2")
p1.call("7", "5")
p1.call("9")
p local
#=> wrong number of arguments (given 1, expected 2) (ArgumentError)
lambda
# 次のコードを実行するとどうなりますか
local = 0
p1 = proc { |arg1, arg2|
arg1, arg2 = arg1.to_i, arg2.to_i
local += [arg1, arg2].max
}
p1.call("1", "2")
p1.call("7", "5")
p1.call("9")
p local
#=> 18
解説
メソッド | 説明 |
---|---|
Kernel#lambda | callする際の引数は省略できない。省略するとArgumentErrorエラーが発生する |
class#proc | callする際の引数を省略できる。省略するとnilが入力される |
参考
問題13. instance_evalの使い方
# 次のプログラムを実行するとどうなりますか
m = Module.new
CONST = "Constant in Toplevel".freeze
_proc = Proc.new do
CONST = "Constant in Proc".freeze
end
m.instance_eval(<<-EOS)
CONST = "Constant in Module instance".freeze
def const
CONST
end
EOS
m.module_eval(&_proc)
p m.const
#=> "Constant in Module instance"
解説
- 特異メソッドを定義するのであれば、instance_evalを使う
- インスタンスメソッドを定義するのであれば、module_evalを使う
メソッド | 説明 |
---|---|
Object#instance_eval | obj.instance_eval { block } 渡されたブロックをレシーバのインスタンスの元で実行します。ブロックの戻り値がメソッドの戻り値になります。 |
Module#module_eval | mod.module_eval { block } ブロックをクラス定義やモジュール定義の中のコードであるように実行します。ブロックの戻り値がメソッドの戻り値になります。 |
module_evalを使った書き方
CONST = 'Constant in Toplevel'.freeze
_proc = proc do
CONST = 'Constant in Proc'.freeze
end
Module.module_eval(<<-EOS)
CONST = "Constant in Module instance".freeze
def const
CONST
end
EOS
Module.module_eval(&_proc)
m = Module.new
p m.const
#=> "Constant in Module instance"
問題14. moduleとself、parentが混じった場合のインスタンスメソッドの探索順序
module M
def foo
super
puts "M#foo"
end
end
class C2
def foo
puts "C2#foo"
end
end
class C < C2
def foo
super
puts "C#foo"
end
include M
end
p C.ancestors
#=> [C, M, C2, Object, Kernel, BasicObject]
C.new.foo
#=> C2#foo
#=> M#foo
#=> C#foo
解説
- ancestorsメソッドを使って優先順位を確認する
[C, M, C2, Object, Kernel, BasicObject]
- self、module、parentの順番であることがわかる
- superは、親が呼ばれるので、Cのsuperは、M
参考
問題15. クラス変数のスコープ
# 次のプログラムを実行するとどうなりますか
module M
@@val = 10
class Parent
@@val = 1
end
class Child < Parent
@@val += 2
end
if Child < Parent
@@val += 3
else
@@val += 4
end
end
p M::Child.class_variable_get(:@@val)
# 3
解説
- クラス変数のスコープはsuperクラスを含む。しかし、Moduleは上位スコープ(外側)なので、スコープ外となる
-
Mの@@val
は上位スコープなのでスコープ外となる -
Parentの@@val
とChildの@@val
は同じスコープとなる
-
- クラス変数は、その場所を囲むもっとも内側の(特異クラスでない) class 式 または module 式のボディをスコープとして持つ
module M
@@val = 10 # M
class Parent
@@val = 1 # M::Child
end
class Child < Parent
@@val += 2 # M::Child
end
if Child < Parent
@@val += 3 # M
else
@@val += 4 # M
end
end
p M::Child.class_variable_get(:@@val)
#=> 3
p M.class_variable_get(:@@val)
#=> 13
参考
問題16. prependの探索順
prependの探索順
# 次のコードを実行するとどうなりますか
module M1
end
module M2
end
class C
prepend M1, M2
end
p C.ancestors
#=> [M1, M2, C, Object, Kernel, BasicObject]
解説
- prependはモジュールのメソッドを
特異メソッドとして追加
する - 特異メソッドとはクラスではなくある特定のオブジェクトに固有のメソッドのこと
- objというオブジェクトがあったとして、obj.mというメソッド呼び出しがあった場合、Rubyは以下の優先順位でメソッドの探索を行う。*Rubyの各メソッドと優先順位の記事を参照
- objの特異メソッド
- objのクラスのmというインスタンスメソッド
- objのクラスがインクルードしているモジュールのインスタンスメソッド(includeされた順とは逆順でモジュールを検索する)
- スーパークラスのインスタンスメソッド
- スーパークラスがインクルードしているモジュールのインスタンスメソッド
- 見つからない場合、method_missingメソッドが呼び出される
- 従って、prependで拡張したメソッドは、selfより探索順が先になる
参考
問題17. Comparable#betweenの使い方
# 次のコードを実行するとどうなりますか
class Company
include Comparable
attr_reader :id
attr_accessor :name
def initialize(id, name)
@id = id
@name = name
end
def to_s
"#{id}:#{name}"
end
def <=> other
self.id <=> other.id
end
end
c1 = Company.new(3, 'Liberyfish')
c2 = Company.new(2, 'Freefish')
c3 = Company.new(1, 'Freedomfish')
p c1.between?(c2, c3)
#=> false
p c2.between?(c3, c1)
#=> true
解説
-
between?
で値を比較するためには、Comparable
をincludeする必要がある- なぜなら、between?は、Comparableのメソッドであるため
- https://docs.ruby-lang.org/ja/2.1.0/method/Comparable/i/between=3f.html
- 自作のクラスで比較する場合は、
def <=> other
で並び順を指定する必要がある- 数値クラス、String、Timeなどの標準クラスであれば、comparableが実装済なので必要なし
メソッド | 説明 |
---|---|
Comparable#between? | obj.between?(min, max) レシーバobjの値が引数minとmaxの間に含まれればtrue、そうでなければfalseを返します。objがminまたはmaxと等しいときはtrueを返します。 |
参考
- https://docs.ruby-lang.org/ja/2.1.0/method/Comparable/i/between=3f.html
- https://docs.ruby-lang.org/ja/2.1.0/class/Comparable.html
- [ruby]値が範囲内に存在するかbetweenでチェック(数、文字、日付)
問題18. 定数の探索順序
# 次のプログラムを実行するとどうなりますか
# 答え: "011"
module M1
class C1
CONST = '001'.freeze
end
class C2 < C1
CONST = '010'.freeze
module M2
CONST = '011'.freeze
class Ca
CONST = '100'.freeze
end
class Cb < Ca
p CONST
#=> "011"
p Module.nesting
#=> [M1::C2::M2::Cb, M1::C2::M2, M1::C2, M1]
end
end
end
end
# 次のプログラムを実行するとどうなりますか
# 答え: "Hello, world"
class C
CONST = 'Good, night'.freeze
end
module M
CONST = 'Good, evening'.freeze
end
module M
class C
CONST = 'Hello, world'.freeze
end
end
module M
class C
p CONST
#=> "Hello, world"
p Module.nesting
#=> [M::C, M]
end
end
解説
- 親クラスとネストのクラスで同名の定数が定義されているとネストの定数の方を先に参照する
-
M1::C2::M2::Cb
とネストされているので、cb -> M2 -> C2 -> M1と定数が探索される
-
- M1,C2,M2,Cbに定数が無かったら、superクラスの
Ca
が探索される - この問題の場合は、
M2
に定数があるので、'011'
と出力される。
ancestors
p M1::C2::M2::Cb.ancestors
#=> [M1::C2::M2::Cb, M1::C2::M2::Ca, Object, Kernel, BasicObject]
参考
問題19. メソッド名と引数の間の空白の扱い
# 次のコードを実行するとどうなりますか
# 答え:256
def foo(n)
n ** n
end
puts foo (2) * 2
#=> 256
解説
-
foo (2) * 2
はメソッド名と引数の間に空白があるため、foo((2) * 2)
が呼ばれたと解釈される。よって、4の4乗の256になる。 - 空白の有無でSyntaxErrorになるなど解釈が異なるので注意が必要
- Rubyでは、メソッド引数のカッコはメソッド名の直後に書くこと。空白を入れちゃだめという記事にも、これに書かれています。
参考
問題20. 自己代入演算子の変数
# 次のコードを実行するとどうなりますか
# 答え:エラー
x = 0
def hoge
(1...5).each do |_i|
x += _i
#> `block in hoge': undefined method `+' for nil:NilClass (NoMethodError)
end
puts x
end
hoge
解説
- そもそも、 メソッドの外と内では、変数名が同一でも、別変数として扱われる
-
1行目のx
とhogeメソッドのx
では別変数として扱われる
-
- 自己代入演算子では、左のオペランドを評価するため、変数が初期化されていないとnilでエラーが発生する
- hogeメソッドの
x += _i
は、ブロック内で、変数定義しているので、nilとなって、NoMethodErrorが発生する
- hogeメソッドの
問題21. rescueの型を省略した場合に捕捉できるErrorと捕捉順序
# 次のコードを実行するとどうなりますか
# 答え:'Error.\nEnsure'
begin
puts 1 + '2'
# TypeErrorが発生!
rescue => ex
puts 'Error.'
rescue TypeError => ex
puts 'Type Error.'
ensure
puts 'Ensure'
end
解説
- rescueで、型を省略すると、StandardErrorとその子クラスの例外を補足する
-
puts 1 + '2'
で発生するTypeErrorは、StandardErrorの子クラスなので、型を省略したrescueにて処理される - TypeErrorを捕捉する場合は、rescure TypeErrorを先に宣言すると想定通りの動作となる
-
TypeErrorを捕捉する場合
begin:
puts 1 + '2'
rescue TypeError => ex
puts 'Type Error.'
rescue => ex
puts 'Error.'
ensure
puts 'Ensure'
end
- Rubyで自前の例外クラスを作るときExceptionではなくStandardErrorを継承する
- http://d.hatena.ne.jp/yarb/20121005/p1
- 理由としては、StandardErrorはアプリケーションErrorで例外処理する。システム障害系は、復旧できない例外として強制終了するという使い分けができると思った
- そういう訳で、rescueで、型を省略すると、StandardErrorが捕捉できるのがRubyの思想だと理解した
参考
問題22. オーバーライドした場合のsuperの実行順序
# 次のコードを実行するとどうなりますか
# 答え:'Hoge'
class Hoge
attr_reader :message
def initialize
@message = 'Hoge'
end
end
class Piyo < Hoge
def initialize
@message = 'Piyo'
super
end
end
puts Piyo.new.message
#=> Hoge
解説
- オーバーライドした場合、superはコードの順序通りに実行される
- Piyo#initializeで、
@message
に格納した後にsuperが呼ばれているので、@message
に'hoge'が格納される
- Piyo#initializeで、
- 以下、superを上に持ってくると、'Piyo'が出力される
superを上に定義した場合
class Hoge
attr_reader :message
def initialize
@message = 'Hoge'
end
end
class Piyo < Hoge
def initialize
super # superを上に持ってきた
@message = 'Piyo'
end
end
puts Piyo.new.message
#=> Piyo
問題23. superの暗黙の引数
# 次のコードを実行するとどうなりますか
# 答え:
# Hello
# Hoge
# Piyo
class Hoge
def say(message)
puts message
end
end
class Piyo < Hoge
def say(message)
super
puts 'Piyo'
end
end
p Piyo.new.say('Hello')
#=> Hello
#=> Hoge
#=> Piyo
# 次のコードを実行するとどうなりますか
# 答え: エラー!!
class Hoge
def say
puts'Hoge'
end
end
class Piyo < Hoge
def say(message)
super
puts 'Piyo'
end
end
p Piyo.new.say('Hello')
#=> `say': wrong number of arguments (given 1, expected 0) (ArgumentError)
解説
- superを引数なしで呼び出すと、子クラスのメソッドの引数が自動的に親クラスのメソッドに渡される
- Ruby:
super
キーワードの4つの側面(翻訳) - しかし、親クラスのメソッドに引数が定義されていない場合、引数が暗黙で親クラスのメソッドに渡されてしまうので、ArgumentErrorが発生してしまう
- Ruby:
- Rubyは、ancestorsチェインで、メソッドを探索し、見つかったメソッドに引数を渡すことがポイント
- ancestorsチェインを調べるには、ancestorsメソッドを使うと調べることが可能
- https://docs.ruby-lang.org/ja/2.1.0/method/Module/i/ancestors.html
参考
問題24. ローカル変数とブロック変数
# 次のコードを実行するとどうなりますか
# 答え: エラー!!
str = '0'
%w[1 2 3].each do |str|
print str + ' '
end
puts str
#=> 1 2 3 0
解説
- ローカル変数とブロック変数では、スコープが異なるので別変数として扱われる
問題25. trueとfalseの条件
# 次のコードを実行するとどうなりますか
# 答え:
# is false.
#false is false.
# is true.
#0 is true.
#1 is true.
#0.0 is true.
[nil, false, ' ', 0, 1, 0.0].each do |val|
if val
puts "#{val} is true."
else
puts "#{val} is false."
end
end
解説
- false は nil オブジェクトとともに偽を表し、 その他の全てのオブジェクトは真となる
-
0
は他言語(例えばC言語)ではFalseと判断される。しかし、Rubyの場合、0
はTrueと判断される点に注意- 詳しい話は「Rubyのtrueとfalseの話」が参考になりました。
参考
問題25. 変数の初期化
# 次のコードを実行するとどうなりますか
a = 1, 2, 3
p a
#=> [1, 2, 3]
a, b = 1, 2, 3
p a
#=> 1
p b
#=> 2
a, *b = 1, 2, 3
p a
#=> 1
p b
#=> [2, 3]
*a, b = 1, 2, 3
p a
#=> [1, 2]
p b
#=> 3
問題26. String#deleteの使い方
# 次のコードを実行するとどうなりますか
"hello".delete "l","lo"
#=> "heo"
"hello".delete "lo"
#=> "he"
"hello".delete "aeiou", "^e"
#=> "hell"
"hello".delete "ej-m"
#=> "ho"
解説
- 指定した文字を取り除いた文字列を生成して返すメソッド
- 「-」は文字列の両端にない場合にだけ範囲指定の意味になる。 「^」も文字列の先頭にあるときだけ効果を発揮します。 また、「-」「^」「\」はバックスラッシュ (「\」) によってエスケープできる
- 引数を複数指定した場合は、 すべての引数にマッチする文字だけが削除される