可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have an array, and I want the result of the first block that returns a truthy value (aka, not nil). The catch is that in my actual use case, the test has a side effect (I'm actually iterating over a set of queues, and pop'ing off the top), so I need to not evaluate the block beyond that first success.
a,b,c = [1,2,3]
[a,b,c].first_but_value{ |i| (i + 1) == 2 } == 2
a == 2
b == 2
c == 3
Any ideas?
回答1:
[1, 2, 3].detect { |i| i += 1; break i if i == 2 }
# => 2
[1, 2, 3].detect { |i| i += 1; break i if i == 10 }
# => nil
回答2:
break
is ugly =P
If you want a functional approach, you want a lazy map:
[nil, 1, 2, 3].lazy.map{|i| i && i.to_s}.find &:itself
# => "1"
If you don't believe it is not iterating throughout the array, just print out and see:
[nil, 1, 2, 3].lazy.map{|i| (p i) && i.to_s}.find &:itself
# nil
# 1
# => "1"
Replace i.to_s
by your block.
回答3:
find_yield
does what you want, check out ruby facets with many core extensions, and especially find_yield
Enumberable method: https://github.com/rubyworks/facets/blob/master/lib/core/facets/enumerable/find_yield.rb
回答4:
Is this what you want to do?
a, b, c = 1, 2, 3
binding.tap { |b|
break b.local_variable_get [ :a, :b, :c ].find { |sym|
b.local_variable_set( sym, b.local_variable_get( sym ) + 1 ) == 2
}
} #=> 2
a #=> 2
b #=> 2
c #=> 3
回答5:
Here's my take, is this closer to your actual use case? Note the content of b
is 3
instead of 2
because my_test_with_side_effect
is called on b
as well.
class MyQueue
def initialize(j)
@j = j
end
def my_test_with_side_effect
(@j+=1) == 2
end
end
(a,b,c) = [MyQueue.new(1),MyQueue.new(2),MyQueue.new(3)]
[a,b,c].each { |i| break i unless i.my_test_with_side_effect }
=> #<MyQueue:0x007f3a8c693598 @j=3>
a
=> #<MyQueue:0x007f3a8c693980 @j=2>
b
=> #<MyQueue:0x007f3a8c693598 @i=3>
c
=> #<MyQueue:0x007f3a8c693430 @i=3>
回答6:
I doubt there's a way to do this. The problem being that Ruby creates a closure in the block and the variable i
is local to it. Doing i+=1
can be expanded to i = i + 1
which creates a new variable i
in the block's scope and doesn't modify the value in any of your a,b,c
variables.