Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 有没有一种优雅的方法可以遍历数组并在结束前停止?_Ruby_Idioms - Fatal编程技术网

Ruby 有没有一种优雅的方法可以遍历数组并在结束前停止?

Ruby 有没有一种优雅的方法可以遍历数组并在结束前停止?,ruby,idioms,Ruby,Idioms,我想写一段代码来做好这一点,无论如何,这是一个更复杂的版本: answer = nil array.each do |e| x = complex_stuff_with(e) if x.is_the_one_i_want? answer = x break end end 这显然不令人满意。像这样遍历数组是一种代码味道 理想情况下,我希望数组中有某种方法,可以让我依次测试每个元素,然后返回我的答案并在有答案时停止。有这样的方法吗 我能做到的最接近的方法是使用brea

我想写一段代码来做好这一点,无论如何,这是一个更复杂的版本:

answer = nil
array.each do |e|
  x = complex_stuff_with(e)
  if x.is_the_one_i_want?
    answer = x
    break
  end
end
这显然不令人满意。像这样遍历数组是一种代码味道

理想情况下,我希望数组中有某种方法,可以让我依次测试每个元素,然后返回我的答案并在有答案时停止。有这样的方法吗

我能做到的最接近的方法是使用break-inside-inject。但我基本上还是在手工进行迭代

answer = array.inject do |_,e|
  x = complex_stuff_with(e)
  break x if x.is_the_one_i_want?
end
更新:我找不到这样的方法似乎是因为不存在这样的方法


更新:说话太早了!我需要懒惰的接线员!谢谢大家。

我想这就是你们想要的:

answer = array.find do |e|
  x = complex_stuff_with(e)
  break x if x.is_the_one_i_want?
end

我从

其他两个选项中选择了此代码,前提是对于您的代码来说,以下简化是可行的:

def complex_stuff_with(e)
  e**2
end

ary = [1, 2, 3, 2] # integer objects
x_i_want = 4 # instead of x.is_the_one_i_want?
因此,在这种情况下:

res = ary.find { |e| complex_stuff_with(e) == x_i_want}.then { |e| e ? x_i_want : :whatever }
#=> 4
或使用:


在这两种情况下,只要第一个元素与条件匹配,迭代就会立即退出。

您实际上要做的是,首先对数组中的元素进行转换,然后是满足某个谓词的第一个转换元素

我们可以这样表示,我使用的是中的定义,但有一个附加的副作用,以便您可以观察转换操作的评估:

def complex_stuff_with(e)
  p "#{__callee__}(#{e.inspect})"
  e**2
end

ary = [1, 2, 3, 2]
x_i_want = 4

ary
  .map(&method(:complex_stuff_with))
  .find(&x_i_want.method(:==))
# "complex_stuff_with(1)"
# "complex_stuff_with(2)"
# "complex_stuff_with(3)"
# "complex_stuff_with(2)"
#=> 4
这给了我们正确的结果,但没有正确的副作用。你希望手术顺利进行。这很简单,我们只需要将

或者,使用问题中的定义:

array
  .lazy
  .map(&method(:complex_stuff_with))
  .find(&:is_the_one_i_want?)
你想要这个

这使得Ruby可以评估整个操作链映射,一次检测每个元素,而不是评估所有元素的映射,然后检测,等等。这使得您可以在映射中执行复杂而昂贵的操作,而不必在整个可枚举项上计算它

举例说明:

expensive_stuff = ->(x) { puts "Doing expensive stuff with #{x}" ; x }
result = (1..Float::INFINITY).lazy.map {|e| expensive_stuff[e] }.detect(&:even?)
puts "Result: #{result}"

# Doing expensive stuff with 1
# Doing expensive stuff with 2
# Result: 2

对不起,那不对。如果本例中的退出值x为真,则find将返回e。我希望该方法返回x。此外,它与我问题中的第二个示例完全相同,方法从inject更改为find?:/是的,对不起,我不记得ruby中的inject方法。我想这可能只是你想要的一个例子,我突然想到这个方法可能就是你想要的方法。@AndyJones由于中断,这个方法按预期工作。这样,该方法将返回参数break,即:x,而不是通常的返回值find。如果没有值是1,则方法将像往常一样返回,find将返回nil。请注意,next将从当前调用的块中返回给定值,break将从调用当前块的方法中立即返回给定值,即在本例中为find。@HolgerJust-好的,它可以工作,但它实际上比我自己的示例更糟糕,因为您希望find从数组返回一个元素。这种行为似乎没有很好的记录。至少使用inject可以清楚地看到,块的结果将在块内计算。如果x是我想要的,也许你可以创建一个方法并使用return x?在循环内部。我不知道你为什么需要它,也不知道你所面临的限制,但我不认为这是正确的bad@LucasWieloch…啊,这是我的第一个例子。除非返回是错误的,否则会留下方法而不是块。您需要break.answer=find_element_xarray和def find_element_xarray。。。结束会让方法回到你想要答案的地方。这对所有相关人员来说都是在浪费时间。我无法删除该问题,并且标记为关闭的原因似乎都不正确。向所有花时间读这篇文章的人道歉…我不得不不同意。你和@LucasWieloch都有行之有效的解决方案,你似乎只是出于个人审美而拒绝了它们。这使这成为一个意见问题,这就是为什么我投票这样做。谢谢!太棒了!而且解释得很清楚。不过,我要给乔格打个招呼,因为他先到了那里。对不起,没问题!我只是略读了一下答案,没有注意到他说的懒惰——这是他应得的:
array
  .lazy
  .map(&method(:complex_stuff_with))
  .find(&:is_the_one_i_want?)
array.lazy.map {|e| expensive_stuff(e) }.detect(&:is_the_one_i_want?)
expensive_stuff = ->(x) { puts "Doing expensive stuff with #{x}" ; x }
result = (1..Float::INFINITY).lazy.map {|e| expensive_stuff[e] }.detect(&:even?)
puts "Result: #{result}"

# Doing expensive stuff with 1
# Doing expensive stuff with 2
# Result: 2