Ruby中的保留字BEGIN或END有什么用途?

23

这是一个非常难以找到的词,因为在大多数情况下,在搜索过程中它们不敏感。

除了文档外,我能找到的最好的东西是在IRB中进行测试。

 BEGIN{puts x = 10}
 10

请查看我的更新答案,以获取文档的直接链接。 - Mchl
抱歉Mchl,那只是一个实验。它让我感到意外,我没有预料到。抱歉Victor,希望你能理解。 - Douglas G. Allen
@all 顺便说一下,如果有人感兴趣的话,我已经重新整理了《Ruby编程语言》第二版的所有代码示例。https://github.com/DouglasAllen/code-Programming_Ruby_2nd_ed - Douglas G. Allen
@Mchl 我把它放回去了,这里是我找到的代码,看起来很像你的。http://docs.ruby-doc.com/docs/ProgrammingRuby/html/language.html#UA - Douglas G. Allen
小意外。这是我引用的地方。我的回答中有一个链接。;) - Mchl
显示剩余3条评论
6个回答

23

所有的关键字BEGINEND都被记录为Object的公共实例方法(尽管你不会从Object.public_instance_methods中看到它们返回)

BEGIN通过代码块指定,在程序顺序执行之前无条件执行的代码。 有时用于模拟对方法的前向引用。

puts times_3(gets.to_i)

BEGIN {
  def times_3(n)
    n * 3
  end
}

END通过代码块指定在程序终止之前执行的代码。

END { 
  puts "Bye!" 
}

来自《Programming Ruby: 代码的艺术》一书的更详细的解释:

BEGIN 和 END 块

每个 Ruby 源文件都可以声明被当作文件加载时运行的代码块(BEGIN 块)以及程序执行完毕后运行的代码块(END 块)。

BEGIN {   
  begin code 
}

END {
  end code 
}

一个程序可以包含多个BEGIN和END块。BEGIN块按照他们被遇到的顺序执行,而END块则按相反的顺序执行。


1
是的!谢谢。我在书的Ruby语言章节和第303页下找到了它。这大概是唯一提到这个的地方,而且只有一个段落。你看到这有多么晦涩了吗?这就是为什么我依赖于这个网站和网络的原因。 - Douglas G. Allen
@DouglasG.Allen 这在许多 Ruby 书籍和网络教程中都有提到。但由于大小写不敏感的问题,它确实很难搜索到。 - Telemachus
@steenslag,你的Runpaint可能被重定向了?我最终会跳转到一些随机的东西。我该如何使用它? - Douglas G. Allen
很遗憾,Runpaint的所有网站似乎都已经消失了(作者有关Vim和Ruby的好资料)。但是你仍然可以间接地获取这些材料,从GitHub repos - Telemachus

19

有一件事情没有被提到,那就是在早期版本的 Ruby 中,BEGIN 是无条件执行的:

if false
  BEGIN { puts "Up is down, hot is cold, good is evil!" }
end

如果您尝试在Ruby 1.8.7中执行此操作,即使它在未执行的if分支中,该句子也会被打印出来。

在Ruby 2.0.0下,使用BEGIN在顶级之外是语法错误(一种更智能的处理方式):

unconditional.rb:2: BEGIN is permitted only at toplevel
  BEGIN { puts "Up is down, hot is cold, good is evil!" }
       ^

编辑:某种程度上,没有人回答你在评论中提出的问题:为什么Ruby需要BEGIN?我来试试。像许多东西一样,BEGIN是从Perl借鉴而来的。Perl 之所以有它,是因为它在awk中存在。在awk中使用它是很有意义的,因为默认情况下,一个awk文件由一系列模式和动作组成:

/foo/ { print $1 }
/bar/ { print $2 }

每行都会检查所有模式,如果匹配则执行操作,否则awk将移至下一个模式。因此,在上面的小脚本中,如果行匹配'foo',则打印第一个字段。如果行匹配'bar',则打印第二个字段。

但是现在你可以看到BEGIN(和END)块所填补的空白:如果您想要做一些无条件的事情,例如在任何输入被测试之前打印标题或在报告结尾处打印一行总计(如何?正常的awk模式+操作行不能帮助你实现。

这就是为什么BEGINEND存在的原因。但我不确定它们对于现代、惯用的Ruby脚本有多有用。但正如dbenhur在评论中指出的那样,你仍然可以很好地使用Ruby编写类似于awk的一行命令。(我还记得标准的Ruby测试库MiniTest曾经使用at_exit函数进行测试,但我不确定它是否仍然在使用。)

两个关于Ruby、awk和Ruby one-liners的好链接:


我开始看到了它的用处,谢谢。假设你开始编写代码时没有考虑任何顺序,只是让它自由流动,但后来你意识到顺序是错误的。也许在这种情况下它会有用呢?这只是一个想法。我们能否问一下 'Matz' 他当初是怎么想的?也许他甚至没有这样做。那么是谁做了呢? - Douglas G. Allen
@DouglasG.Allen 我更新了我的回答,试图解释Ruby中BEGINEND的历史。 - Telemachus
1
“我不确定它们对于现代的、惯用的 Ruby 有多大用处。” Ruby 仍然具有 awk 模式 -an-ap。它非常适用于组合 awk 风格的命令行单行程序,通常比 awk 本身更具能力和表现力。 - dbenhur
@dbenhur 我同意你的观点,但是我指的是Ruby脚本形式而不是单行命令。不过谢谢你的建议,我会在我的回答中加上这一点。 - Telemachus

7

以下内容翻译自 《Ruby编程语言》


BEGINEND 代码块

每个 Ruby 源文件都可以声明在加载文件时运行的代码块(BEGIN 代码块)和程序执行完后运行的代码块(END 代码块)。

BEGIN {
  # begin code
} 
END {
  # end code
}

一个程序可能包含多个BEGINEND块。 BEGIN块按照遇到的顺序执行。END块按照相反的顺序执行。


因此:

$ cat beginend.rb
END { puts :end }
BEGIN { puts :begin }
END { puts :end2 }
BEGIN { puts :begin2 }

puts :run
$ ruby beginend.rb 
begin
begin2
run
end2
end

5

BEGIN块就是你想象的那样,即给定的块将在程序中其余代码之前运行。

以下是一个例子。

puts "Goodbye cruel world!"

BEGIN {
puts "Hello World!"
}

我希望能够帮助您。
这里有一个示例代码(点击链接查看),它展示了如何在minitest中将一组值放到文件末尾,并首先进行计算。

很好的例子 Victor。我可以在我关于保留字的维基百科中使用吗? - Douglas G. Allen
根据本网站的许可证,我认为你只应受到该许可证的限制。 - vgoff

2

BEGIN/END在使用-e选项处理流时非常方便。例如,要对数字文件进行求和:

cat <<EOF > numbers
1
5 
10
20
EOF

cat numbers | ruby -ane 'BEGIN { $t=0}; END {puts $t}; $t += $_.to_i'

请注意,BEGIN将全局变量归零,END输出结果。

0

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