块作为参数传递吗?

4
我可以这样向函数传递参数:
func 1, 2, 3

或者我可以使用括号,例如:
func(1, 2, 3)

后来我了解到像 list.each 这样的函数,我会传入(不确定这是否是真正发生的)一个块来操作每个元素:

list.each {|x| puts x}

我认为这只是将块作为参数传递给each函数,但事实并非如此,因为:
list.each( {|x| puts x} )

无法正常工作。

当我看到以下内容时,意识到了这一点:

5.upto(9) {|x| puts x}

如果块只是一个参数,那么这根本没有意义。

这是怎么回事?你可以指向任何资源来帮助解释这个问题,以及其他不明显的结构问题吗?


你可能会发现这是一个有用的起点:http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/ - Chris
这是一种专门为函数传递块而创建的特殊语法。对于每个函数调用,您可以在常规参数列表之外传递一个块。该块仍会在被调用的函数上下文中附加到参数列表中。 - user229044
@Matt - 既然你正在学习Ruby,不妨看看这个网站:http://www.codecademy.com/tracks/ruby - 这是一个非常好的资源,适合任何想要学习Ruby语言核心基础的人。 - Deej
@David,我就在这里! :) - Matthew Sainsbury
@Matt 保持专业,继续努力! - Deej
啊,小草蜢,这是一个很好的问题。 :-) Ruby是一种很棒的编程语言,而块是其中非常强大的一部分。你会发现它们是语言中许多事情发生的关键。 - the Tin Man
2个回答

6

块确实有点特殊,但也可以用作参数。考虑这个函数:

def greet
  yield "Hello"
end

greet{ |greeting| puts "#{greeting} to you" }

你同样也可以这样写:

def greet(&block)
  block.call("Hello")
end

greet{ |greeting| puts "#{greeting} to you" }

# which is equivalent to:
my_proc = proc{ |greeting| puts "#{greeting}, nice to see you." }
greet(&my_proc)

最后,块是使用特殊语法的Proc的一种特殊形式,这使它们更易于使用。但你仍然可以访问并传递Procs。

4
+1. 我想补充一点,这里有一些需要注意的地方。块(Blocks)在技术上是 Proc 对象,类似于但并不完全相同于 lambda 对象。它们在处理可变参数等方面的行为并不完全相同,例如。(诚然,这可能与 Ruby 的 bug 有关,自从我最后一次使用 Ruby 进行真正大量工作以来可能已经得到修复,但当时存在着如此生动的细微差别。)还要注意,通过使用 &block 语法,在每次调用时都会显式创建一个 Proc 对象——即使没有传递块,在没有必要创建对象的情况下会导致代码速度变慢。 - Denis de Bernardy

4

方法可以接收一个块作为特殊参数。

def foo
  yield 123
end

foo { |x| puts x }
#=> 123

注意这个方法接受零个参数,但仍然接受一个块。这是因为将一个块挂在一个方法上的语法是一种特殊情况。任何传递给方法的块都可以使用yield关键字运行。你甚至可以使用block_given?关键字查询是否传递了块。
如果你想将它捕获到一个本地变量中,你可以使用一些特殊的语法。
def foo(a, &block)
  block.call a
end

foo(123) { |x| puts x }
#=> 123

在这种情况下,你需要在参数列表中显式地捕获块参数,并使用该&前缀。现在你有了一个变量,可以向它发送call消息以执行。请注意,这必须出现在参数列表的末尾。
还要注意括号。 foo(123) { |x| puts x }。参数列表停止,括号关闭,附加的块随后而来。同样,这是语法中的特殊情况。

你可以这样调用吗:def two_func_in(&func1, &func2)?那个调用会是什么样子的?编辑:你说“方法只能接受一个块”,这就是这个问题的答案吗?谢谢。 - Matthew Sainsbury
def two_func_in(&func1, &func2) 不是有效的 Ruby 代码。你只能将一个块附加到一个方法上。但是,还有其他方法可以将一些代码作为基本简单参数传递进去。例如 Proc 对象,虽然处理它的语法不同。 - Alex Wayne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接