Ruby Enumerable:块的第一个真值

sun*_*ess 6 ruby enumerable

在 ruby​​ 中我们可以做这样的事情:

stuff_in_trash.detect(&:eatable?)
=> :pack_of_peanuts

stuff_in_trash.detect(&:drinkable?)
=> nil
Run Code Online (Sandbox Code Playgroud)

但是,如果我们对块第一次为真时的值感兴趣,而不是块取真值的第一个项目,该怎么办?

也就是转换下面的代码:

def try_to_make_artwork_from(enumerable)
  enumerable.each do |item|
    result = make_artwork_from item
    return result if result
 end
   nil
end
Run Code Online (Sandbox Code Playgroud)

对于类似的事情:

def try_to_make_artwork_from(enumerable)
  enumerable.try_with { |item| make_artwork_from item }
end
Run Code Online (Sandbox Code Playgroud)

初始代码中需要的内容是:

  1. nil如果该块从未采用真值,则返回;
  2. 它在第一次为真时返回块的值;
  3. 找到第一个匹配后停止;
  4. 它不会make_artwork_from再次调用(假设它不能保证下次调用它时返回相同的结果)。

不太理想的是,使用了result三次,但与故事无关。

编辑:抱歉,最初的实现是不正确的,它需要在nil块值从来不为真的情况下返回。

enumerable.lazy.map(&:block).detect(&:itself)
Run Code Online (Sandbox Code Playgroud)

可以完成这项工作,但这是最简单的方法吗?each与简单地使用并缓存值相比,它是否有效?

Eri*_*nil 4

\n

它可以完成工作,但这是最简单的方法吗?与简单地使用each并缓存值相比,它是否有效?

\n
\n\n

最简单的方法?

\n\n

我们可以定义这个方法:

\n\n
def first_truthy_block(enumerable, &block)\n  enumerable.lazy.map(&block).find(&:itself)\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n

在这里行动:

\n\n
array = [0,1,2,3,4,:x5,\'abc\']\n\nputs first_truthy_block(array) { |x|\n  if x ** 2 > 10 then\n    "ARTWORK with #{x}!!!"\n  end\n} \n#=> ARTWORK with 4!!!\n
Run Code Online (Sandbox Code Playgroud)\n\n

能更简单一点吗?

\n\n
    \n
  • enumerable是需要的,它是您正在处理的对象。
  • \n
  • lazy需要时,它不会尽快停止,并且会抛出异常:x5**2
  • \n
  • 需要地图,您需要对您的元素应用一些方法
  • \n
  • 需要 find 才能从可枚举中最多提取一个值
  • \n
\n\n

使用标准Enumerable方法,我不认为它会变得更简单。

\n\n

有效率吗?

\n\n

它比你的each方法慢。它基本上做同样的事情,并且应该具有相同的复杂性,但它确实使用更多的方法调用并创建更多的对象:

\n\n
require \'fruity\'\n\ndef first_truthy_block_lazy(enumerable, &block)\n  enumerable.lazy.map(&block).find(&:itself)\nend\n\ndef first_truthy_block_each(enumerable, &block)\n  enumerable.each do |item|\n    result = block.call(item)\n    return result if result\n end\n   nil\nend\n\nbig_array = Array.new(10_000){rand(4)} + [5] + Array.new(10_000){rand(20)} + [:x, :y, \'z\']\n\ncompare do\n  _lazy_map do\n    first_truthy_block_lazy(big_array) { |x|\n      if x ** 2 > 10 then\n        "ARTWORK with #{x}!!!"\n      end\n    }\n  end\n\n  _each do       \n    first_truthy_block_each(big_array) { |x|\n      if x ** 2 > 10 then\n        "ARTWORK with #{x}!!!"\n      end\n    }\n  end\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n

果味回报:

\n\n
Running each test once. Test will take about 1 second.\n_each is faster than _lazy_map by 3x \xc2\xb1 0.1\n
Run Code Online (Sandbox Code Playgroud)\n