ようへいの日々精進XP

よかろうもん

俺は Ruby について 1bit も理解していなかったので写経する 〜 文法編 〜

これは

  • Ruby Gold を受験するにあたって学んでいること等を写経したり, メモったりしています
  • Ruby 技術者認定試験合格教本」を主に参考にしており, 今回は第三章「文法」を写経していきます

gihyo.jp

そして, この記事は

既に 2018 年だけど...

qiita.com

初老丸 Advent Calendar 2017 24 日目の記事です.

変数

初期化されていな時に参照した場合の動作

ローカル変数

  • 参照箇所より前に代入文が記述してある場合は nil
  • 代入文が記述されていない場合には例外発生
irb(main):001:0> p foo
NameError: undefined local variable or method `foo' for main:Object

グローバル変数

irb(main):004:0* p $foo
nil
=> nil

クラス変数

(irb):5: warning: class variable access from toplevel
NameError: uninitialized class variable @@foo in Object

インスタンス変数

irb(main):007:0* p @foo
nil
=> nil

定数

irb(main):009:0> p FOO
NameError: uninitialized constant FOO

リテラル

Rubyリテラル

  • 数値
  • 論理値
  • 文字列
  • シンボル
  • 配列
  • ハッシュ
  • 範囲
  • 正規表現
  • コマンド出力

数値リテラル

有理数, 複素数リテラル

irb(main):010:0> 42/10r
=> (21/5)
irb(main):011:0> (42/10r).class
=> Rational
irb(main):012:0> 3.14r
=> (157/50)
irb(main):013:0> 42i
=> (0+42i)
irb(main):014:0> 42i.class
=> Complex
irb(main):015:0> 3.14i
=> (0+3.14i)

数値演算

irb(main):016:0> 100 <=> 10
=> 1
irb(main):017:0> 100 <=> 100
=> 0
irb(main):018:0> 10 <=> 100
=> -1

UFO 演算子と呼ばれる比較演算子は, 比較の結果を数値で返す. 左辺の値が右辺の値より大きければ 1 , 等しければ 0 , 左辺の値が小さければ -1 を返す.

irb(main):019:0> a = 100
=> 100
irb(main):020:0> a += 1
=> 101
irb(main):021:0> a -= 1
=> 100
irb(main):022:0> a *= 1
=> 100
irb(main):023:0> a *= 2
=> 200
irb(main):024:0> a **= 2
=> 40000

数値クラス

irb(main):025:0> 1.class
=> Fixnum
irb(main):026:0> 1.class.superclass
=> Integer
irb(main):027:0> 1.class.superclass.superclass
=> Numeric

ブロックと Proc

ブロックの基本

ブロックはメソッドを呼び出すときのみ記述でき, メソッド内部では, yield という式を使用することでブロックの内部で記述した処理を呼び出すことが出来る.

def func x
  x + yield
end

p func(1){ 2 }

以下は実行例.

irb(main):028:0> def func x
irb(main):029:1>   x + yield
irb(main):030:1> end
=> :func
irb(main):031:0> p func(1){ 2 }
3
=> 3

クロージャとしてのブロック

def func y
  y + yield
end

x = 2
p func(1) { x +=2 }

以下は実行例.

irb(main):032:0> def func y
irb(main):033:1>   y + yield
irb(main):034:1> end
=> :func
irb(main):035:0> x = 2
=> 2
irb(main):036:0> p func(1) { x += 2 }
5
=> 5
irb(main):037:0> p x
4
=> 4
  • ブロック外で変数 x に値 2 を代入している
  • メソッド func にブロックを渡して yield によりブロックを実行
  • ブロック実行した際に x の値を取得し更新する(この x はブロックの外の変数 x と同一の変数である)
  • ブロック内で x を更新(x += 2)している為, func メソッドの実行後, x の値を参照すると値が更新されていることが分かる

ユーザーから直接指定できず, 変更も出来ないので, 代入ではなく束縛と呼ぶ. このように処理の生成時の環境を束縛するものを, 一般的にクロージャと呼ぶ.

ブロックフォーマット

  • ブロックは引数を受け付けることができ, 波括弧 {} 又は do の後で引数リストを | で囲む
def func a, b
  a + yield(b, 3)
end

p func(1, 2){|x, y| x + y}

以下, 実行例.

[1] pry(main)> def func a, b
[1] pry(main)*   a + yield(b, 3)
[1] pry(main)* end  
=> :func
[2] pry(main)> p func(1, 2){|x, y| x + y}
6
=> 6

上記の例では,

  • func に 1 と 2 を引数として渡している
  • func の内部では, 第一引数の値 1 とブロックの実行結果を合計する
  • yield はブロック引数(2 と 3)の値を合計して 5 が返り, 実行結果は 6 となる

ブロックの判定

メソッドの内部でブロックが指定されたかどうかを判定する例.

def func
  return 1 if block_given?
  2
end

p func()[]
p func

以下, 実行例.

[1] pry(main)> def func
[1] pry(main)*   return 1 if block_given?
[1] pry(main)*   2
[1] pry(main)* end  
=> :func
[2] pry(main)> p func(){}
1
=> 1
[3] pry(main)> p func
2
=> 2

Proc

  • Proc クラスのコンストラクタに, ブロックを指定することで生成する
  • Proc を実行する場合, Proc のインスタンスに対して call メソッドを使用する
proc = Proc.new{|x| p x}
proc.call(1)

以下, 実行例.

[4] pry(main)> proc = Proc.new{|x| p x}
=> #<Proc:0x00563922227520@(pry):7>
[5] pry(main)> proc.call(1)
1
=> 1

Proc とブロックの相互変換

  • Proc からブロックへの変換
def func x
  x + yield
end

proc = Proc.new {2}
func(1, &proc)

以下, 実行例.

[1] pry(main)> def func x
[1] pry(main)*   x + yield
[1] pry(main)* end  
=> :func
[2] pry(main)> proc = Proc.new {2}
=> #<Proc:0x0056494c0d5098@(pry):4>
[3] pry(main)> func(1, &proc)
=> 3

上記のように, Proc オブジェクトに & を付けて最後の引数に指定する.

  • ブロックから Proc への変換
def func x, &proc
  x + proc.call
end

func(1) do
  2
end

以下, 実行例.

[4] pry(main)> def func x, &proc
[4] pry(main)*   x + proc.call
[4] pry(main)* end  
=> :func
[5] pry(main)> func(1) do
[5] pry(main)*   2
[5] pry(main)* end  
=> 3
  • ブロックを Proc オブジェクトとして受けるには, 最後の仮引数に & を付けた名前を指定する
  • & を付けた引数で Proc オブジェクトを参照出来る
  • 参照時は & を付けないこと

lambda

lambda メソッドは, Proc インスタンスを生成するが, Proc とは異なる動きをする.

lmb = lambda{|x| p x}
# Ruby 1.9 から以下のような書き方も出来る
lmb = ->(x){p x}
lmb.call(1)

以下, 実行例.

[6] pry(main)> lmb = lambda{|x| p x}
=> #<Proc:0x0056494cbf9e60@(pry):12 (lambda)>
[7] pry(main)> lmb.call(1)
1
=> 1
[8] pry(main)> lmb = ->(x){p x}
=> #<Proc:0x0056494cc794d0@(pry):14 (lambda)>
[9] pry(main)> lmb.call(1)
1
=> 1

proc と lambda の違い

  • Proc では生成元のスコープを脱出する
  • lambda は, そのブロック内で return すると呼び出し元に復帰する
# proc 中のリターン
def func
  proc = Proc.new{return 1}
  proc.call
  2
end

p func

# lambda 中のリターン
def func
  proc = lambda{return 1}
  proc.call
  2
end

p func

以下, 実行例.

[10] pry(main)> def func
[10] pry(main)*   proc = Proc.new{return 1}
[10] pry(main)*   proc.call
[10] pry(main)*   e
[10] pry(main)* end  
=> :func
[11] pry(main)> p func
1
=> 1
[12] pry(main)> def func
[12] pry(main)*   proc = lambda{return 1}
[12] pry(main)*   proc.call
[12] pry(main)*   2
[12] pry(main)* end  
=> :func
[13] pry(main)> p func
2
=> 2

以下の通り, 引数が一致しない場合, メソッドでは例外 ArgumentError が発生するが, Proc とブロックでは無視するか, nil を代入する.

[14] pry(main)> def func x
[14] pry(main)*   x
[14] pry(main)* end  
=> :func
[15] pry(main)> func
ArgumentError: wrong number of arguments (0 for 1)
from (pry):28:in `func'
[16] pry(main)> p1 = Proc.new{|x, y| y}
=> #<Proc:0x0056494cf472c0@(pry):32>
[17] pry(main)> p p1.call(1)
nil
=> nil
[18] pry(main)> p p1.call(1, 2)
2
=> 2

lambda の場合には, 以下のようにメソッドと同じ動きになる.

[19] pry(main)> p1 = lambda{|x, y| y}
=> #<Proc:0x0056494cd82278@(pry):35 (lambda)>
[20] pry(main)> p p1.call(1)
ArgumentError: wrong number of arguments (1 for 2)
from (pry):35:in `block in __pry__'
[21] pry(main)> p p1.call(1, 2)
2
=> 2

-> を用いた lambda 記法

Ruby 1.9 以降から lambda を -> の形式で書くことが出来る.

p1 = ->(x, y){p x + y}
p p1.call(x, y)

以下, 実行例.

[22] pry(main)> p1 = ->(x, y){p x + y}
=> #<Proc:0x0056494cbfa040@(pry):38 (lambda)>
[23] pry(main)> p p1.call(1, 2)
3
3
=> 3

ブロックを受け付けるメソッド

配列の each メソッド

[1, 2, 3].each do |value|
  p value
end

# 同義
[1, 2, 3].each {|value| p value}

配列のインデックスが必要な場合には, each_with_index メソッドが使用可能.

[3, 4, 5].each_with_index do |value, index|
  puts index + value
end

# 同義
[3, 4, 5].each_with_index {|value, index| puts index + value}

引数を二つとり, 第二引数にインデックスが指定される.

ハッシュの each メソッド

{:a => 1, :b => 2}.each do |key, value|
  p "#{key}:#{value}"
end

# 同義
{:a => 1, :b => 2}.each {|key, value| p "#{key}:#{value}"}

キーのみ必要な場合 each_key メソッド, 値のみ必要な場合は each_value メソッドが使用出来る.

# each_key
{:a => 1, :b => 2}.each_key do |key|
  p "#{key}"
end

# 同義
{:a => 1, :b => 2}.each_key {|key| p "#{key}"}

# each_value
{:a => 1, :b => 2}.each_value do |value|
  p "#{value}"
end

# 同義
{:a => 1, :b => 2}.each_value {|value| p "#{value}"}

範囲オブジェクトの each メソッド

("a".."d").each do |value|
  p value
end

# 同義
("a".."d").each {|value| p value}

範囲を指定したループでは, 値を増やしていく upto メソッドや値を減らしていく downto メソッドもある.

2.upto(4) do |i|
  p i
end

# 同義
2.upto(4) {|i| p i}
# 実行例
[34] pry(main)> 2.upto(4) {|i| p i}
2
3
4

5.downto(1) do |i|
  p i
end

# 同義 1
5.downto 1 do |i|
  p i
end
# 同義 2
5.downto(1) {|i| p i}
# 実行例
[35] pry(main)> 5.downto(1) {|i| p i}
5
4
3
2
1

do ~ end と {}

do ~ end より {} の方が結合が強い

うっかり do ~ end{} が同義だと思い込んでいたけど, るりまこちらを読ませて頂いて, 以下のような違いがあるようだ.(「るりま」より引用)

{ ... } の方が do ... end ブロックよりも強く結合します。 次に例を挙げますが、このような違いが影響するコードは読み辛いので避けましょう:

foobar a, b do .. end   # foobarの引数はa, bの値とブロック
foobar a, b { .. }      # ブロックはメソッドbの引数、aの値とbの返り値とがfoobarの引数

今までは, コードを見易くする為だけに使い分けをしてきた気がするけど, 以下のように, 用途毎に書き分けるようにしたいと思う.(こちらより引用)

{}

  • ブロック付きメソッドがインラインの場合
  • ブロック付きメソッドの戻り値を利用する場合
  • ブロック付きメソッドからさらにメソッドチェーンする場合
  • リソース管理のためにブロックを使う場合

do ~ end

スレッド

Thread クラス

Thread クラスをインスタンス化することで新しいスレッドを生成することが出来る.(new や start 及び fork でインスタンスを生成する)

t = Thread.new do
  p "start"
  sleep 5
  p "end"
end
p "wait"
t.join

生成したインスタンスの join メソッドによって, スレッドの終了を待つことが出来る.(join メソッドでスレッドの終了を待つ)

ファイバ

Thread クラスとの相違点

  • Thread は平行処理しているタスクの切り替えを OS や仮想マシンが行う
  • ファイバは切り替えのタイミングを開発者がプログラム内で明示的に行う

Fiber

f = Fiber.new do
  (1..3).each do |i|
    Fiber.yield i
  end
  nil
end
p f.resume

以下, 実行例.

[48] pry(main)> f = Fiber.new do
[48] pry(main)*   (1..3).each do |i|
[48] pry(main)*     Fiber.yield i
[48] pry(main)*   end  
[48] pry(main)*   nil
[48] pry(main)* end  
=> #<Fiber:0x0056494c6db460>
[49] pry(main)> p f.resume
1
=> 1
[50] pry(main)> p f.resume
2
=> 2
[51] pry(main)> p f.resume
3
=> 3
[52] pry(main)> p f.resume
nil
=> nil
[53] pry(main)> p f.resume
FiberError: dead fiber called
from (pry):83:in `resume'

ファイバへのコンテキストを切り替えるには resume メソッドを利用する. resume を呼び出すと, 対象のファイバ内の処理を終了するか, Fiber.yield が呼び出されるまで, ファイバ内のよりを実行する.

脱出構文と例外処理, 大域脱出

脱出構文

next

10.times do |i|
  next if i == 5
  print i, ""
end

以下, 実行例.

[1] pry(main)> 10.times do |i|
[1] pry(main)*   next if i == 5
[1] pry(main)*   print i, ""
[1] pry(main)* end  
012346789=> 10

i が 5 の時, next が実行されて次のループに進む.

redo

10.times do |i|
  redo if i == 5
  print i, ""
end

以下, 実行例.

[1] pry(main)> 10.times do |i|
[1] pry(main)*   redo if i == 5
[1] pry(main)*   print i, ""
[1] pry(main)* end  
01234 ... #=> 無限ループ

i が 5 の時, i が 5 のループをやり直す為, 4 を表示した後で無限ループとなる.

例外処理

raise

  • 例外を発生させる為には, raise を使用する
  • raise は第一引数に例外クラス又はそのインスタンスを, 第二引数にメッセージを指定する
raise ArgumentError, "引数が不正です"
raise ArgumentError.new, "引数が不正です"

以下, 実行例.

[3] pry(main)> raise ArgumentError, "引数が不正です"
ArgumentError: 引数が不正です
[4] pry(main)> raise ArgumentError.new, "引数が不正です"
ArgumentError: 引数が不正です

例外クラスのコンストラクタではメッセージを指定出来るので, 以下のように書くことも可能.

err = ArgumentError.new("引数が不正です")
raise err

以下, 実行例.

[5] pry(main)> err = ArgumentError.new("引数が不正です")
=> #<ArgumentError: 引数が不正です>
[6] pry(main)> raise err
ArgumentError: 引数が不正です

例外クラスのインスタンスを省略した場合は, RuntimeError クラスの例外が発生する.

[8] pry(main)> raise "実行中にエラーが発生しました."
RuntimeError: 実行中にエラーが発生しました.

begin ~ end

処理を中断させずに続行するには, 例外処理を記述する必要がある. 例外が発生する可能性がある箇所を begin ~ end で囲み, その中の rescue という節に例外処理を記述する.

begin
  1 / 0
  p 1
rescue
  p 0
end

以下, 実行例.

[9] pry(main)> begin
[9] pry(main)*   1 / 0 # 例外が発生
[9] pry(main)*   p 1
[9] pry(main)* rescue  
[9] pry(main)*   p 0   # 実行される
[9] pry(main)* end  
0
=> 0

else 節と ensure 節

resucue 節に続いて else 節を指定することで, 例外が発生しなかった時の処理を記述出来る. ensure 節を設けることで, 例外の発生に関わらず, 必ず実行する処理も追加出来る.

begin
  p 1
rescue
  p 0
else
  p 2
ensure
  p 3 
end

以下, 実行例.

[10] pry(main)> begin  # begin 節は実行される
[10] pry(main)*   p 1
[10] pry(main)* rescue # 例外は発生しないので, rescue 節は実行しない 
[10] pry(main)*   p 0
[10] pry(main)* else   # rescue 節が実行されないので else 節は実行される
[10] pry(main)*   p 2
[10] pry(main)* ensure # ensure 節は必ず実行される 
[10] pry(main)*   p 3
[10] pry(main)* end  
1
2
3
=> 2

rescue は, begin 節を指定しなくても使用出来る. rescue 節は, if 式と同様に修飾子として書くことが出来る.

[12] pry(main)> 1 / 0 rescue p 1
1
=> 1

例外クラスを指定した捕捉

例外クラスの階層

  • Exception
    • ScriptError
      • SyntaxError
    • Signal Exception
    • StandardError
      • ArgumentError
      • RuntimeError
      • NameError => NoMethodError
      • ZeroDivisionError

主な例外クラスは以下の通り.

例外クラス名 発生する場面の例
SyntexError 文法エラーがあった場合
SignalException 捕捉していないシグナルを受けた場合
ArgumentError 引数の数が合わない場合や値が正しくない場合
RuntimeError 特定の例外クラスには該当しないエラーが発生した場合や例外クラスを省略した raise の呼び出し
NameError 未定義のローカル変数や定数を参照した場合
NoMethodError 未定義のメソッドを呼び出した場合
ZeroDivisionError 整数に対して整数の 0 で除算を行った場合
  • 各例外クラスを rescue に続けて指定することで, それ自身か, そのサブクラスを捕捉することが出来る
  • 例外クラスに続いて => で識別子を指定すると, 例外オブジェクトを参照出来る
  • 例外オブジェクトは, message メソッドを使用して指定した例外メッセージを参照出来る
  • 同様に backtrace オブジェクトを使用して例外が発生した場所を参照出来る
begin
  1/0
rescue ZeroDivisionError => e
  p e
  p e.message
  p e.backtrace
end

以下, 実行例.

$ ruby 3-161.rb 
#<ZeroDivisionError: divided by 0>
"divided by 0"
["3-161.rb:2:in `/'", "3-161.rb:2:in `<main>'"]
  • 同じスレッドとブロックで発生した最後の例外は, 組み込み関数 $! で参照することが出来る
  • raise メソッドを引数無しで実行することで, 最後に発生した例外を再度発生させることが出来る
begin
  1/0
rescue ZeroDivisionError => e
  p $!
  raise
end

以下, 実行例.

$ ruby 3-162.rb 
#<ZeroDivisionError: divided by 0>
3-162.rb:2:in `/': divided by 0 (ZeroDivisionError)
        from 3-162.rb:2:in `<main>'

catch/throw による大域脱出

階層の深いループの中で全ての処理が完了した場合のように, 正常時であっても処理を抜けたい場合に catch と throw をペアで利用する.

def foo
  throw :exit
end

catch(:exit) {
  foo
  p 1
}
  • throw が例外の場合の raise に相当し, catch が begin 節に相当すると考える
  • throw が実行されると, 同盟のラベルが指定されている catch まで呼び出しスタックを辿り, ラベルが見つかった場合には, そのブロック内における後続の処理をスキップする
  • ラベルには, シンボルの他に文字列を指定出来る
  • 対応するラベルが見つからない場合は, NameError 例外が発生する

問題

多重代入

以下を実行すると何が表示されるか.

x, *y = *[0, 1, 2]
p x, y
  • 左辺の x には, 最初の要素が格納される
  • y には * が付いているので, 残りの要素が配列として格納される

実行例.

[3] pry(main)> x, *y = *[0, 1, 2]
=> [0, 1, 2]
[4] pry(main)> p x,y
0
[1, 2]
=> [0, [1, 2]]

数値リテラルの演算

実行結果が 0.8 とならないコードはどれか.

  1. 4/5
  2. 4.0/5
  3. 4/5r
  4. 4/5.0

  5. Integer 同士の演算は Integer となり, 小数点以下は丸められる

  6. Integer と Rational クラスの演算は Rational クラスのオブジェクトを生成する((4/5r).to_f とすれば 0.8 になる)

実行例.

[5] pry(main)> 4/5
=> 0
[6] pry(main)> 4.0/5
=> 0.8
[7] pry(main)> 4/5r
=> (4/5)
[8] pry(main)> 4/5.0
=> 0.8

例外

以下のコードを実行するとどうなるか.

class Err1 < StandardError; end
class Err2 < Err1; end
begin
  raise Err2
rescue => e
  puts "StandardError"  
rescue Err2 => ex
  puts ex.class 
end
  • begin 節の raise で発生する例外オブジェクトのクラスは Err2
  • rescue => e にて, StandardError から派生する全ての例外を対象とする為, StandardError が出力される

以下, 実行例.

[1] pry(main)> class Err1 < StandardError; end
=> nil
[2] pry(main)> class Err2 < Err1; end
=> nil
[3] pry(main)> begin
[3] pry(main)*   raise Err2
[3] pry(main)* rescue => e  
[3] pry(main)*   puts "StandardError"
[3] pry(main)* rescue Err2 => ex  
[3] pry(main)*   puts ex.class
[3] pry(main)* end  
StandardError
=> nil

定数の更新

以下のコードを実行するとどうなるか.

class C
  VAR = 0
  def VAR=v
    VAR = v
  end
  def VAR
    VAR
  end  
end

c = C.new
c.VAR = 3
puts c.VAR

以下, 実行例.

$ ruby kihon-5.rb 
kihon-5.rb:4: dynamic constant assignment
    VAR = v
         ^

ブロック

以下のコードの中で文法として正しいものを選ぶ.

1.upto 5 do |x|
  puts x
end
1.upto(5) do |x|
  puts x
end
1.upto 5 {|x| puts x }
1.upto(5) {|x| puts x }
  • ブロック引数を { ... } で囲む場合には, 引数の () を省略出来ない
  • do ... end で囲む場合には, () を省略することが出来る

以下, 実行例.

[1] pry(main)> 1.upto 5 do |x|
[1] pry(main)*   puts x
[1] pry(main)* end  
1
2
3
4
5
=> 1
[2] pry(main)> 1.upto(5) do |x|
[2] pry(main)*   puts x
[2] pry(main)* end  
1
2
3
4
5
=> 1
[3] pry(main)> 1.upto 5 {|x| puts x }
SyntaxError: unexpected '{', expecting end-of-input
1.upto 5 {|x| puts x }
          ^
[3] pry(main)> 1.upto(5) {|x| puts x }
1
2
3
4
5
=> 1

Proc

以下の実行結果となるように適切なコードを選ぶ.

<p>Hello, World.</p>

適切なコード(1).

def tag(name)
  puts "<#{name}>#{yield}</#{name}>"
end
tag(:p) {"Hello, world"}

適切なコード(2).

def tag(name, &block)
  puts "<#{name}>#{block.call}</#{name}>"
end
tag(:p) {"Hello, world"}

ブロック付きメソッドから呼び出し元ブロックを実行するには...

  • yield を使う
  • 引数に & を付けた変数を定義して, ブロックを Proc オブジェクトとして取得してから Proc#call を呼び出す

以下, 実行例.

[4] pry(main)> def tag(name)
[4] pry(main)*   puts "<#{name}>#{yield}</#{name}>"
[4] pry(main)* end  
=> :tag
[5] pry(main)> tag(:p) {"Hello, world"}
<p>Hello, world</p>
=> nil
[6] pry(main)> def tag(name, &block)
[6] pry(main)*   puts "<#{name}>#{block.call}</#{name}>" 
[6] pry(main)* end  
=> :tag
[7] pry(main)> tag(:p) {"Hello, world"}
<p>Hello, world</p>
=> nil

可変長引数

以下の実行結果となるように適切なコードを選ぶ.

[1, 2, 3]

適切なコード.

def hoge(*args)
  p *args
end

hoge [1, 2, 3]
  • * が付いたメソッド引数は可変長引数になる
  • hoge に配列 [1, 2, 3] を渡すと, args[0] に格納されるので, 実行結果の出力を得る為には *args 又は args[0] を指定する

以下, 実行例.

[8] pry(main)> def hoge(*args)
[8] pry(main)*   p *args
[8] pry(main)* end  
=> :hoge
[9] pry(main)> hoge [1, 2, 3]
[1, 2, 3]
=> [1, 2, 3]
[10] pry(main)> def hoge(*args)
[10] pry(main)*   p args[0]
[10] pry(main)* end  
=> :hoge
[11] pry(main)> hoge [1, 2, 3]
[1, 2, 3]
=> [1, 2, 3]

キーワード引数

以下の実行結果となるように適切なコードを選ぶ.

1, 2, 3

適切なコード.

def hoge(x:, y: 2, **params)
  puts "#{x}, #{y}, #{params[:z]}"
end

hoge x:1, z:3
  • キーワード引数
  • 引数前に ** をつなげることで, 明示的に定義したキーワード以外の引数を Hash オブジェクトとして受け取ることが出来る

以下, 実行例.

[12] pry(main)> def hoge(x:, y: 2, **params)
[12] pry(main)*   puts "#{x}, #{y}, #{params[:z]}"
[12] pry(main)* end  
=> :hoge
[13] pry(main)> hoge x:1, z:3
1, 2, 3
=> nil

lamda 式

以下の実行結果となるように適切なコードを選ぶ.

"Hello World"

適切なコード.

hi = ->(x){ puts "Hello, #{x}" }
p hi.call("World")

以下, 実行例.

[14] pry(main)> hi = ->(x){ puts "Hello, #{x}" }
=> #<Proc:0x005643d17ebb28@(pry):28 (lambda)>
[15] pry(main)> p hi.call("World")
Hello, World
nil
=> nil

以上

Ruby 技術者認定試験合格教本」第三章を中心に写経したメモでした.