在 Ruby 中,if/elsif/else 语句的从属块与作为参数传递的"块"相同吗?



我正在阅读Ruby中的if/elsif/else,在描述控件表达式的工作原理时,我在术语上遇到了一些差异。

在Ruby编程维基教科书中(着重号是加的(:

条件 Branch 获取测试表达式的结果,并根据测试表达式是真还是假执行代码块

例如,if 表达式不仅确定是否执行从属代码块,而且还生成值本身。

但是,Ruby-doc.org 在定义中根本没有提到块:

最简单的 if 表达式有两个部分,一个 "test" 表达式和一个 "then"表达式。如果"test"表达式的计算结果为true,则计算"then"表达式。

通常,当我读到Ruby中的"块"时,它几乎总是在procs和lambdas的上下文中。例如,rubylearning.com 定义了一个块:

Ruby 块是一种对语句进行分组的方式,可能只出现在方法调用旁边的源中;该与方法调用的最后一个参数(或参数列表的右括号(在同一行开始编写。

问题:

  • 当谈论 Ruby 中的代码块时,我们是在谈论 传递给方法的代码组,或者我们只是 一般谈论一组代码?
  • 没有办法轻松区分两者(并且有没有 两者之间的技术差异(?

这些问题的上下文:我想知道将条件中的代码称为是否会让新的 Ruby 程序员在以后被引入块、过程和 lambda 时感到困惑。

TL;DRif...end是一个表达式,而不是一个块

Ruby 中术语block的正确用法是传递给介于do...end或大括号{...}之间的方法的代码。通过使用方法签名中的&block语法,块可以并且经常隐式转换为方法中的Proc。这个新Proc是一个具有自己的方法的对象,可以传递给其他方法,存储在变量和数据结构中,重复调用等......

def block_to_proc(&block)
prc = block
puts prc
prc.class
end
block_to_proc { 'inside the block' }
# "#<Proc:0x007fa626845a98@(irb):21>"
# => Proc

在上面的代码中,隐式创建了一个Proc,将块作为其主体并分配给变量block。类似地,一个Proc(或lambda,一种Proc(可以通过在参数列表末尾使用&block语法"扩展"为块并传递给期望它们的方法。

def proc_to_block
result = yield # only the return value of the block can be saved, not the block itself
puts result
result.class
end
block = Proc.new { 'inside the Proc' }
proc_to_block(&block)
# "inside the Proc"
# => String

虽然blockProc之间有一条双向的街道,但它们并不相同。请注意,要定义Proc我们必须将block传递给Proc.new。严格来说,block只是传递给方法的一段代码,该方法的执行被延迟到显式调用。Proc是用block定义的,它的执行也会延迟到被调用,但它是一个真正的对象,就像任何其他对象一样。block不能靠自己生存,Proc可以。

另一方面,blockblock of code有时被随意地用来指代以end结尾的Ruby关键字所包围的任何谨慎的代码块:if...else...endbegin...rescue...enddef...endclass...endmodule...enduntil...end。但这些本身并不是真正的块,只是在表面上真正类似于它们。通常,它们还会延迟执行,直到满足某些条件。但它们可以完全独立存在,并且始终具有返回值。Ruby-doc.org对"表达式"的使用更准确。

来自维基百科

编程语言中的表达式是 或 更明确的值、常量、变量、运算符和函数 编程语言解释(根据其特定 优先规则和关联规则(并计算产生("至 返回",在有状态环境中(另一个值。

这就是为什么你可以做这样的事情

return_value = if 'expression'
true
end
return_value # => true

尝试使用块执行此操作

return_value = do
true
end
# SyntaxError: (irb):24: syntax error, unexpected keyword_do_block
# return_value = do
#                  ^

块本身不是一个表达式。它需要yield或转换为Proc才能生存。当我们将一个块传递给一个不需要的方法时会发生什么?

puts("indifferent") { "to blocks" }
# "indifferent"
# => nil

块完全丢失了,它消失了,没有返回值,没有执行,就好像它从未存在过一样。它需要yield才能完成表达式并生成返回值。

class Object
def puts(*args)
super
yield if block_given?
end
end
puts("mindful") { "of blocks" }
# "mindful"
# => "of blocks"

最新更新