将 lambda 作为块传递
Posted
技术标签:
【中文标题】将 lambda 作为块传递【英文标题】:Passing a lambda as a block 【发布时间】:2011-12-31 00:38:39 【问题描述】:我正在尝试定义一个块,用于传递多个范围的 each 方法。我不想在每个范围内重新定义块,而是创建一个lamba,并像这样传递lambda:
count = 0
procedure = lambda |v| map[count+=1]=v
("A".."K").each procedure
("M".."N").each procedure
("P".."Z").each procedure
但是,我收到以下错误:
ArgumentError:参数数量错误(1 代表 0) 来自 code.rb:23:in `each'有什么想法吗?
【问题讨论】:
【参考方案1】:在参数上添加一个 & 符号 (&
),例如:
("A".."K").each &procedure
这表示您将它作为方法的特殊块参数传递。否则它被解释为一个正常的参数。
它还反映了您在方法本身中捕获和访问块参数的方式:
# the & here signifies that the special block parameter should be captured
# into the variable `procedure`
def some_func(foo, bar, &procedure)
procedure.call(foo, bar)
end
some_func(2, 3) |a, b| a * b
=> 6
【讨论】:
【参考方案2】:诀窍在于使用&
,它告诉Ruby 在必要时将此参数转换为Proc
,然后将该对象用作方法的块。从 Ruby 1.9 开始,有一个 lambda(匿名)函数的快捷方式。所以,你可以这样写代码:
(1..5).map &->(x) x*x
# => [1, 4, 9, 16, 25]
将获取数组的每个元素并计算其幂
和这段代码一样:
func = ->(x) x*x
(1..5).map &func
对于 Ruby 1.8:
(1..5).map &lambda |x| x*x
# => [1, 4, 9, 16, 25]
为了解决你的问题,你可以使用Array的方法reduce
(0
是初始值):
('A'..'K').reduce(0) |sum,elem| sum + elem.size
# => 11
将 lambda 函数传递给 reduce
有点棘手,但匿名块与 lambda 几乎相同。
('A'..'K').reduce(0) |sum, elem| ->(sum) sum + 1.call(sum)
# => 11
或者你可以像这样连接字母:
('A'..'K').reduce(:+)
=> "ABCDEFGHIJK"
转换为小写:
('A'..'K').map &->(a) a.downcase
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
在方法定义的上下文中,在最后一个参数前面放置一个 & 符号表示方法可以采用一个块,并为我们提供一个名称来引用方法体中的这个块。
【讨论】:
【参考方案3】:其他答案留下了一些我想扩展的未澄清内容。我们如何将 arg 和 block 传递给方法?
假设我们有一个方法接受一个 arg 和一个块:
def method_with_arg_and_block(arg)
puts arg
yield
end
和一个过程:
pr = proc puts 'This is a proc'
答案:重要的是您将 proc 作为带有 & 符号的 arg 传递,而不是将 proc 附加到方法中(就像使用块一样)。
例如,如果你这样做:
method_with_arg_and_block('my arg') &pr
你会得到一个“no block given (yield)”异常。
正确的称呼是:
method_with_arg_and_block('my arg', &pr)
Ruby 会负责将 proc 转换为块并将其附加到您的方法中。注意:由于 lambdas 也是 procs,所以这也适用于 lambdas。
感谢https://medium.com/@sihui/proc-code-block-conversion-and-ampersand-in-ruby-35cf524eef55 帮助我理解这一点。
【讨论】:
以上是关于将 lambda 作为块传递的主要内容,如果未能解决你的问题,请参考以下文章