Ответ 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
У меня есть массив, и я хочу, чтобы результат первого блока возвращал истинное значение (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, 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
Это то, что вы хотите сделать?
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
find_yield
делает то, что вы хотите, проверяете рубиновые грани со многими расширениями ядра и особенно find_yield
метод Enumberable: https://github.com/rubyworks/facets/blob/master/lib/core/facets/enumerable/find_yield.rb
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"
Вот мое мнение, это ближе к вашему фактическому варианту использования? Обратите внимание, что содержимое 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>
Я сомневаюсь, что есть способ сделать это. Проблема в том, что Ruby создает замыкание в блоке, а переменная i
является локальной для него. Выполнение i+=1
можно расширить до i = i + 1
, который создает новую переменную i
в области видимости блока и не изменяет значение ни в одной из ваших переменных a,b,c
.