Ruby 迭代器产量

Ruby iterator yield

我想知道为什么以下标记方法会产生不同的结果:

方法一:

def tag(html)
  print "<#{html}>#{yield}</#{html}>"
end

方法二:

def tag(html)
  print "<#{html}>"
  print yield
  print "</#{html}>"
end

当我运行使用上述方法在终端中输入以下代码时:

tag(:ul) do
  tag(:li) { "It sparkles!" }
  tag(:li) { "It shines!" }
  tag(:li) { "It mesmerizes!" }
end

第一个给了我:

<li>It sparkles!</li><li>It shines!</li><li>It mesmerizes!</li><ul></ul>

第二个给了我:

<ul><li>It sparkles!</li><li>It shines!</li><li>It mesmerizes!</li></ul>

第二个是我正在寻找的输出。

为什么第一个方法在打印字符串中 'yield' 之前的内容之前先打印 'yield'?

主要问题是操作顺序。当您调用 print 时,它会立即打印 ,没有延迟,这可能会导致问题。

在 Ruby 中,处理 return 字符串的代码通常比处理导致打印等副作用的代码更容易。如果它们 return 字符串,您可以控制输出的位置。如果他们立即打印东西,您需要非常注意调用它们的顺序。

您在最终汇编中使用 tag(:ul) 调用调用该代码的方式实际上会很麻烦。您的方法的第二个版本巧合地正确排序。

解决这个问题并不容易。如果您 return 一个字符串,则只会使用三个 tag 调用中的最后一个字符串。如果您打印,则必须确保您使用的是第二种方法才能使其正常工作。

在 Rails 系统中,有一种方法可以捕获这些东西的输出以用于缓冲目的,但这是一件非常麻烦的事情,当你尝试处理所有情况时,它会变得非常混乱.

尽可能创建一些缓冲区,这些东西可以写入,然后当一切都完成后,用 print 或其他什么写出来。

只是回应@tadman 的回答:api 的评估顺序和不一致。您的块有时会 returns 字符串,有时会打印字符串作为副作用。

  print "<#{html}>"
  print yield
  print "</#{html}>"

这里打印,然后yield。如果块 returns 是一个字符串(:li 块之一),那么它就打印在这里。如果它是一个 :ul 块,那么 它的副作用 会发生(打印 li 块)并且之后打印 nil。

另一种情况

print "<#{html}>#{yield}</#{html}>"

Ruby 必须 assemble 一个字符串才能打印。这意味着在任何打印之前屈服。这意味着在 打印开头 <ul> 之前 会发生副作用。由于 ul 块 returns nil,这就是它在字符串末尾打印为空的原因 (<ul></ul>).

有道理吗?