Ruby で暗黙のブロックパラメータを使うための gem をつくりました。
- 元ネタ : Rubyのブロックつらい問題を解決する暗黙のブロックパラメータ
- github : osyo-manga/gem-use_arguments
インストール
$ gem install use_arguments
定義される暗黙のブロックパラメータ
パラメータ | 意味 |
---|---|
_1, _2..._N |
各引数の要素 |
_ |
_1 と同じ |
_args |
全引数の配列 |
_yield |
ブロック引数 |
_self |
呼び出された Proc オブジェクト自身 |
_receiver |
ブロックを渡したメソッドのレシーバ |
使い方
使い方は簡単で、ブロックの引数を定義する代わりに _1
や _2
といったプレースホルダを変わりに使用します。
require "use_arguments"
# Array クラスのメソッドに対して使用する
using UseArguments::Array
p [1, 2, 3].map { _1 + _1 }
# => [2, 4, 6]
# Hash クラスのメソッドに対して使用する
using UseArguments::Hash
data = {homu: 13, mami: 14, mado: 13}
p data.select { _2 < 14 }
# => {:homu=>13, :mado=>13}
Proc#use_args
対象の Proc
オブジェクトのブロック内で暗黙のパラメータを使えるようにする。
require "use_arguments"
using UseArguments
p proc { _args }.use_args.call 1, 2, 3
# => [1, 2, 3]
plus = proc { _1 + _2 }.use_args
p plus.call 1, 2
# => 3
fact = proc { _1 == 1 ? 1 : _1 * _self.(_1 - 1); }.use_args
p fact.call 5
# => 120
f = proc { _yield 1, 2 }.use_args
p f.call { |a, b| a - b }
# => -1
p f.call &plus
# => 3
# 引数が配列の場合は配列を展開して受け取る
p proc { _1 + _1 }.use_args.call [1, 2]
# => 2
# lambda の場合は引数を展開しない
p lambda { _1 + _1 }.use_args.call [1, 2]
# => [1, 2, 1, 2]
Object#use_args
#use_args
を呼び出すことでメソッドのブロック内で暗黙のパラメータを使えるようにする。
require "use_arguments"
using UseArguments
p [1, 2, 3].use_args.map { _1 * _1 }
# => [1, 4, 9]
p [1, 2, 3].use_args.select { _1 % 2 == 0 }
# => [2]
using UseArguments::{任意のクラス名}
指定したクラスのメソッドに渡すブロック内で暗黙のパラメータを使えるようにする。
require "use_arguments"
using UseArguments::Array
# もしくは
# using UseArguments.usable Array
p [1, 2, 3].map { _1 + _1 }
# => [2, 4, 6]
p [[1, 2], [3, 4]].map { _1 + _2 }
# => [3, 7]
using UseArguments::Hash
data = {homu: 13, mami: 14, mado: 13}
p data.select { _2 < 14 }
# => {:homu=>13, :mado=>13}
p data.map { "#{_1}:#{_2}" }
# => ["homu:13", "mami:14", "mado:13"]
[注意点] 配列を渡した場合は配列が展開されて引数を受け取る
use_args
に配列を渡した場合、その配列を展開して引数を受け取ります。
p proc { _1 + _2 }.use_args.call [1, 2]
# => 3
これは次のように引数を受け取っていることを想定した挙動になっています。
p proc { |_1, _2| _1 + _2 }.call [1, 2]
# => 3
この挙動はブロックを proc
か lambda
で定義したのかで変わります。
proc
の場合は引数の配列を展開し、lambda
の場合は引数の配列を展開しません。
proc { [_1, _2] }.use_args.call [1, 2] # => 3
lambda { [_1, _2] }.use_args.call [1, 2] # => Error
# proc の場合は渡されなかった引数のパラメータは nil を返す
proc { [_1, _2] }.use_args.call 1 # => [1, nil]
# lambda の場合はエラー
lambda { [_1, _2] }.use_args.call 1 # => Error
# 配列を展開する
[[1, 2], [3, 4]].use_args.map &proc{ _1 + _1 }
# => [2, 6]
# 配列を展開しない
[[1, 2], [3, 4]].use_args.map &lambda{ _1 + _1 }
# => [[1, 2, 1, 2], [3, 4, 3, 4]]
各メソッドに対して do 〜 end
をブロックとして渡した場合は proc
として扱われるので、引数の扱いを厳密にしたい場合は lambda
を使用してください。
その他
use_arguments では引数以外にも _receiver
(メソッドを呼び出したレシーバオブジェクト)や _self
(自分自身)などが定義されています。
なので、引数意外にも次のようなコードを書くことができます。
# 再帰
fact = proc { _1 == 1 ? 1 : _1 * _self.(_1 - 1); }.use_args
p fact.call 5
# => 120
p [1, 2, 3].use_args.map { _1 + _receiver.size }
# => [4, 5, 6]
このようにブロックをちょっと便利に書くこともできます。