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

In Ruby, is an if/elsif/else statement's subordinate block the same as a 'block' that is passed as a parameter?

我正在阅读 Ruby 中关于 if/elsif/else 的内容,并且在描述控制表达式的工作原理时 运行 发现了一些术语上的差异。

Ruby Programming Wikibooks(强调已加):

A conditional Branch takes the result of a test expression and executes a block of code depending whether the test expression is true or false.

An if expression, for example, not only determines whether a subordinate block of code will execute, but also results in a value itself.

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

The simplest if expression has two parts, a “test” expression and a “then” expression. If the “test” expression evaluates to a true then the “then” expression is evaluated.

通常,当我在 Ruby 中阅读有关 'blocks' 的内容时,它几乎总是在过程和 lambda 的上下文中。例如,rubylearning.com定义了一个块:

A Ruby block is a way of grouping statements, and may appear only in the source adjacent to a method call; the block is written starting on the same line as the method call's last parameter (or the closing parenthesis of the parameter list).

问题:

这些问题的背景:我想知道将条件语句内部的代码称为 是否会让新的 Ruby 程序员感到困惑块、过程和 lambda。

TL;DR if...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(或 lambdaProc 的一种类型)可以 "expanded" 放入块中,并传递给需要它们的方法,方法是使用 &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

虽然 blocks 和 Procs 之间有一条双向街道,但它们并不相同。请注意,要定义 Proc,我们必须将 block 传递给 Proc.new。严格来说 block 只是传递给方法的代码块,该方法的执行被推迟到明确调用为止。 Proc 是用 block 定义的,它的执行也被推迟到被调用时,但它和其他对象一样是一个真正的对象。 block 不能靠自己生存,Proc 可以。

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

来自wikipedia

An expression in a programming language is a combination of one or more explicit values, constants, variables, operators, and functions that the programming language interprets (according to its particular rules of precedence and of association) and computes to produce ("to return", in a stateful environment) another value.

这就是为什么你可以这样做

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

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

class Object
  def puts(*args)
    super
    yield if block_given?
  end
end

puts("mindful") { "of blocks" }
# "mindful"
# => "of blocks"