Ruby中的管道符号有什么作用?

79

在Ruby中,竖线符号的作用是什么?

我正在学习Ruby和RoR,之前主要使用PHP和Java。但我一直看到这样的代码:

def new 
  @post = Post.new

  respond_to do |format|
    format.html # new.html.erb
    format.xml { render :xml => @post }
  end
end

在这里,|format|是什么意思?在PHP/Java中,相当于这些管道符号的语法是什么?

7个回答

58

它们是传递给块的变量。

def this_method_takes_a_block
  yield(5)
end

this_method_takes_a_block do |num|
  puts num
end

输出结果为"5"。一个更加晦涩的例子:

def this_silly_method_too(num)
  yield(num + 5)
end

this_silly_method_too(3) do |wtf|
  puts wtf + 1
end
输出结果为“9”。

|wtf| 块结束时,wtf 去哪里了? - ThorSummoner
它的作用范围局限于代码块内部,因此在块外部无法访问。换句话说:它会消失。 - August Lilleaas
1
它是否可变?必须是的,否则我不知道Vagrantfile如何将其用作配置方法。 - ThorSummoner
不确定我是否理解你的问题。它只是一个对象的命名引用。 - August Lilleaas
Mutable 意味着你可以持续更改命名引用,而不像 immutable 无法更改,例如如果您想要保留更改,则必须重新分配变量的突变值。 - ThorSummoner
我不确定在块内部是否可以更改'wtf'指向的内容,或者如果无法更改会出现什么样的错误。但是它可以随着运行程序的进程而改变,因为它本质上与函数参数相同,保存着调用yield的调用方传递的任何值。 - August Lilleaas

38
这也很奇怪,一开始我也感到困惑,但希望这个解释/演示能对你有所帮助。
文档涉及了这个主题,解释得非常好 - 如果我的回答没有帮助到您,我相信他们的指南会有用。
首先,在终端中输入irb并按下Enter启动交互式Ruby解释器。
输入类似以下内容:
the_numbers = ['ett','tva','tre','fyra','fem'] # congratulations! You now know how to count to five in Swedish.

只是为了有一个可以操作的数组。然后我们创建循环:

the_numbers.each do |linustorvalds|
    puts linustorvalds
end

它将输出所有数字,以换行符分隔。

在其他语言中,您需要编写类似于:

for (i = 0; i < the_numbers.length; i++) {
    linustorvalds = the_numbers[i]
    print linustorvalds;
}

需要注意的重要事项是,|thing_inside_the_pipes|可以是任何东西,只要你一直使用它。另外要明白的是,我们在谈论循环,这是我在后来才明白的。


7
在某些“其他语言”(如Python)中,您可以编写for linustorvalds in the_numbers do print linustorvalds。可能更加直观一些。 - Steve Bennett
3
完全正确。我选择 C 风格的语法是因为我怀疑更多接触 Ruby 的人熟悉那种语法,但我也希望他们能阅读你的评论。 - Martin Josefsson
谢谢。"其他语言"部分帮助我理解了发生了什么。+1。 - rayryeng

7
@names.each do |name|
  puts "Hello #{name}!"
end

http://www.ruby-lang.org/en/documentation/quickstart/4/中,each方法接受一个代码块并为列表中的每个元素运行该代码块。在doend之间的部分就是这样的一个代码块。一个代码块类似于一个匿名函数或lambda。管道字符之间的变量是该代码块的参数。
这里发生的事情是,对于列表中的每个条目,name绑定到该列表元素,然后使用该名称运行表达式puts "Hello #{name}!"

7
doend的代码定义了一个Ruby块。单词format是块的参数。该块随方法调用一起传递,被调用的方法可以向块中yield值。
有关详细信息,请参阅有关Ruby的任何文本,这是您将经常看到的Ruby的核心功能。

1
不,do和end之间的代码是一个Ruby块。竖线之间的术语是该块的参数。 - rampion
如果我没记错的话,管道语法是从Smalltalk借鉴来的。 - John Topley
是的,除了Smalltalk之外,只使用了一个管道。 - Chuck

4
在Java中的等效内容可能类似于:
// Prior definitions

interface RespondToHandler
{
    public void doFormatting(FormatThingummy format);
}

void respondTo(RespondToHandler)
{
    // ...
}

// Equivalent of your quoted code

respondTo(new RespondToHandler(){
    public void doFormatting(FormatThingummy format)
    {
        format.html();
        format.xml();
    }
});

Brent,如果你的意思是Java版本很啰嗦 - 是的,我同意。 但另一方面,这在Java中是一种不太惯用的结构。Ruby经常使用它,而Java使用得较少。 - Jon Bright
format.html(); format.xml(); 这部分可能更像是一个 switch 语句,因为您将根据所请求的格式进行切换。 - jonnii
一个更简洁的示例是 JavaScript 中等价的构造:respond_to(new function(format) { format.html(); format.xml(); });我发现 Java 缺乏真正的函数支持真的很烦人;它们并不难使用 - 除非你是在 Java 的匿名类中学习它们! - Reid Rankin

3

块的参数位于|符号之间。


2
为了更加清晰,如果需要的话:
管道符号实质上创建一个新变量来保存先前方法调用生成的值。类似于:
你的方法的初始定义:
def example_method_a(argumentPassedIn)
     yield(argumentPassedIn + 200)
end

如何使用:

example_method_a(100) do |newVariable|
    puts newVariable;
end

这几乎与写下以下内容一样:

newVariable = example_method_a(100) 
puts newVariable

在这里,新变量 = 200 + 100 = 300 :D!


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