Ruby idiom to shortcircuit使用each和map返回第一个非nil

时间:2016-08-29 20:07:50

标签: ruby each

这是我尝试做的事情的本质,但是'打破'没有权利:

needle = nil
haystacks.each do |haystack|
  needle = haystack.look_for_needle()
  break if needle
end

这个更短,但它会遍历每个草堆(不看至少),即使它不需要:

needle = nil
haystacks.each do |haystack|
  needle ||= haystack.look_for_needle()
end

这是一个单行,但(我相信)它不是懒惰的,所以它做了不必要的工作:

needle = hackstacks.map{|h| h.look_for_needle()}.detect{|x| !x.nil?}

我觉得应该有一个班轮,但我不确定会不会:

needle = hackstacks.some_find_each_first_detect_lazy_map_thingee {|h| h.look_for_needle()}

5 个答案:

答案 0 :(得分:6)

使用Ruby 2.x lazy enumerators

needle = haystacks.lazy.map(&:look_for_needle).reject(&:nil?).first

或者:

needle = haystacks.lazy.map(&:look_for_needle).detect { |needle| !needle.nil? }

或者:

needle = haystacks.lazy.map(&:look_for_needle).detect(&:itself)

答案 1 :(得分:2)

haystack.find &:itself
haystack.index &:itself

您更喜欢哪一个?

答案 2 :(得分:1)

我认为find_proverbial_needle_in_a_haystacklook_for_needle都会返回针头或nil,后者如果没有大海捞针包含针头。

class Haystack
  def initialize(haystack)
    @haystack = haystack
  end

  # Suppose look_for_needle is defined as follows
  def look_for_needle
    @haystack.include?(:straw) && :straw
  end 
end

def find_proverbial_needle_in_a_haystack(haystacks)
  needle = nil # can be anything
  haystacks.find { |haystack| needle = haystack.look_for_needle } && needle
end

find返回块评估true的第一个干草堆,如果在任何干草堆中找不到针,则返回nil

haystacks = [Haystack.new([:ball, :top]),
             Haystack.new([:fluff, :straw]),
             Haystack.new([:dog, :cat])]
  #=> [#<Haystack:0x007fdaaa0f6860 @haystack=[:ball, :top]>,
  #    #<Haystack:0x007fdaaa0f67e8 @haystack=[:fluff, :straw]>,
  #    #<Haystack:0x007fdaaa0f6590 @haystack=[:dog, :cat]>] 
find_proverbial_needle_in_a_haystack(haystacks)
  #=> :straw 

haystacks = [Haystack.new([:ball, :top]),
             Haystack.new([:fluff, :yellow_stuff]),
             Haystack.new([:dog, :cat])]
  #=> [#<Haystack:0x007fdaaa082f50 @haystack=[:ball, :top]>,
  #    #<Haystack:0x007fdaaa082f00 @haystack=[:fluff, :yellow_stuff]>,
  #    #<Haystack:0x007fdaaa082eb0 @haystack=[:dog, :cat]>]     
find_proverbial_needle_in_a_haystack(haystacks)
  #=> nil

答案 3 :(得分:1)

对于Ruby 2.0及更高版本,我使用此方法扩展了Enumerable:

module Enumerable
  TRUTHY_LAMBDA = lambda {|result| !!result }
  def first_result(detect_result_lambda=TRUTHY_LAMBDA, &block)
    self.lazy.collect(&block).detect {|result| detect_result_lambda.call(result) }
  end
end

因此您可以致电:

haystacks.first_result {|haystack| haystack.find_needle() }

..它将返回针。

如果需要确保它是was缝针,可以执行以下操作:

haystacks.first_result(lambda {|needle| needle&.for_quilting? }) {|haystack| haystack.find_needle() }

或使用do块格式化的相同代码:

haystacks.first_result(lambda {|needle| needle&.for_quilting? }) do |haystack|
  haystack.find_needle()
end

P.S。 -如果有人可以告诉我如何改进此代码以支持以下语法,那将是惊人的(我收到错误消息“ SyntaxError:同时给出了块arg和实际块”,这向我暗示了Ruby正在分配“&:for_quilting? “到&block而不是第一个参数)

haystacks.first_result(&:for_quilting?) {|haystack| haystack.find_needle() }

答案 4 :(得分:0)

有一种first方法正是如此。

它需要一个块并返回块的第一个匹配

match = haystacks.first { |x| x.find_needle }

https://ruby-doc.org/core-2.2.0/Array.html#method-i-first

顺便说一句,您可以使用eachmap而无需更多按键:

match = haystacks.each { |x| break x if x.find_needle }
match = haystacks.map { |x| break x if x.find_needle }

虽然没有真正使用eachmap的正确语义,您希望返回一个数组(each返回原始数组并map返回一个转换后的数组)