LoginSignup
5
4

More than 5 years have passed since last update.

Ruby高階関数の実験

Last updated at Posted at 2017-03-29

RSpecのexpectが

a = -> { } # 本当のソースはlet使ったけど

expect(a)
expect(&a)

というようにaでも&aでもどちらでも渡せたのでどうやるのかと思って、
自分で書いてみた。(RSpecのexpectと同じかどうかは知らない)

下記 piyoに注目。aでも&aでもどちらでも渡せた。さらに{ }の形式でも渡せる。
上記サンプルに書かなかったが、expect{ }で渡せる。

a = -> { p 'AA' }

def hoge(procedure)
  procedure.call
end

def foo(&procedure)
  procedure.call
end

def piyo(procedure = nil, &procedure2)
  if block_given?
    procedure2.call
  else
    procedure.call
  end
end

##############################
# ここから呼び出し処理
##############################
hoge(a)
# hoge(&a) #=> 無理
# hoge { 'BB' } #=> 無理
foo(&a) #=> foo(a) はできない
foo { p 'BB' }
piyo(a)
piyo(&a)
piyo { p 'CC' }

出力結果

"AA"
"AA"
"BB"
"AA"
"AA"
"CC"

piyoをRubyぽく書き直す

ということでpiyo関数をさらに発展させる。
どう発展させるかというとRubyの機能を使ってRubyぽく書き直す。
この発展させた関数をfugaとしたのが下記ソース。

yieldを使えば第2引数を書く必要がなくなった。

a = -> { p 'AA' }

def fuga(procedure = nil)
  if block_given?
    yield
  else
    procedure.call
  end
end

fuga(a)
fuga(&a)
fuga { p 'CC' }

.call のシンタックスシュガー .() を使う

.callはシンタックスシュガーがあるので下記のように書き換えれる

a = -> { p 'AA' }

def fuga(procedure = nil)
  if block_given?
    yield
  else
    procedure.()  # シンタックスシュガー
  end
end

fuga(a)
fuga(&a)
fuga { p 'CC' }

じゃあ明示的に第2引数(最後にブロックを受け取る引数)を書きたいときはどんなとき?

受けっとた手続きをさらに別の引数に渡したいときに明示的に変数に入れたいのでこの場合は第2引数を書くらしい。

def my_map(procedure = nil, &procedure2)
  r = if block_given?
    (0..2).map(&procedure2) #=> ここでさらに別のメソッドの引数に渡している
  else
    (5..7).map(&procedure)
  end

  p r.to_a
end

a = ->(v){v}

my_map(a)
my_map(&a)
my_map {|v| v * 2}

結果:

[5, 6, 7]
[0, 1, 2]
[0, 2, 4]
5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4