Ruby 中什么时候需要管道输入? (|x| x.to_s)

When are piped inputs necessary in Ruby? (|x| x.to_s)

我最近发现许多 ruby 方法使用的管道输入在某些时候并不是必需的,但这似乎是不一致的。例如,此行为昨天、今天和明天创建了一个字符串数组:

DateTime.now.instance_eval{[prev_day, to_datetime, tomorrow]}.map{|d| d.strftime('%m/%d/%Y')}
=> ["06/20/2016", "06/21/2016", "06/22/2016"]

正如您在 instance_eval 中看到的那样,没有管道输入,该函数只是假设在 DateTime.now 上调用了这些方法。但是如果我尝试将相同的想法应用于 map 方法:

DateTime.now.instance_eval{[prev_day, to_datetime, tomorrow]}.map{strftime('%m/%d/%Y')}
NoMethodError: undefined method `strftime' for main:Object

突然想用main上的方法?

我的问题是为什么这在第一种方法而不是第二种方法中起作用?

来自docs for instance_eval

Evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj). In order to set the context, the variable self is set to obj while the code is executing, giving the code access to obj's instance variables and private methods.

instance_eval 一方面非常特殊,因为您在 实例 上调用它并且它被显式构建到 运行 中的块实例的上下文。因此,该方法始终可以假定 self 的上下文始终是调用 instance_eval 的实例。

另一方面,

map 是不同的:在您的示例中,您在 数组 上调用它,但块应该 运行 在 数组中的每个对象,因此您需要在每次迭代中将不同的对象传递给块。

When are piped inputs necessary in Ruby? (|x| x.to_s)

当一个块被传递给任何带有参数的 yield 方法时,管道输入是必需的。

在您的特定情况下,instance_eval 的 yield 没有参数,而 map 有。您遇到的问题与 instance_eval 无关。如果 yield 希望你传递一个变量,但你没有这样做,那么它就被迫假设 self 是主要对象。这正是您使用 map 的第二个示例中发生的情况。看看这个例子。

def yield_to_integer # yield integer 5 to the block
  yield 5
end  

yield_to_integer {|int| int.chr} # successfully run .chr() on the Integer 5
#  => "\x05"
yield_to_integer {chr} # fails to run .chr() because it is not defined on Object
# NameError: undefined local variable or method `chr' for main:Object