Ruby - Array.find, но верните значение в блок

У меня есть массив, и я хочу, чтобы результат первого блока возвращал истинное значение (aka, not nil). Уловка заключается в том, что в моем фактическом прецеденте тест имеет побочный эффект (я на самом деле повторяю множество очередей и выскакиваю сверху), поэтому мне не нужно оценивать блок за пределами этого первого успеха.

a,b,c = [1,2,3]

[a,b,c].first_but_value{ |i| (i + 1) == 2 } == 2

a == 2
b == 2
c == 3

Любые идеи?

Ответ 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

Это то, что вы хотите сделать?

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

Ответ 4

break ужасен = P

Если вам нужен функциональный подход, вам нужна ленивая карта:

[nil, 1, 2, 3].lazy.map{|i| i && i.to_s}.find{|i| i}    
# => "1"

Если вы не верите, что он не повторяется по всему массиву, просто распечатайте и посмотрите:

[nil, 1, 2, 3].lazy.map{|i| (p i) && i.to_s}.find{|i| i}
# nil
# 1
# => "1"

Ответ 5

Вот мое мнение, это ближе к вашему фактическому варианту использования? Обратите внимание, что содержимое b - это 3 вместо 2, потому что my_test_with_side_effect также вызывается для b.

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

Я сомневаюсь, что есть способ сделать это. Проблема в том, что Ruby создает замыкание в блоке, а переменная i является локальной для него. Выполнение i+=1 можно расширить до i = i + 1, который создает новую переменную i в области видимости блока и не изменяет значение ни в одной из ваших переменных a,b,c.