2016-07-04 4 views
2

Почему это, что:Почему мой блок выполняется только один раз?

array = (1..20).to_a 
array.index.each_slice(5) do |slice| 
    puts slice.inspect 
end 

возвращается:

[1, 2, 3, 4, 5] 
[6, 7, 8, 9, 10] 
[11, 12, 13, 14, 15] 
[16, 17, 18, 19, 20] 

в то время как:

other_array = [] 
array = (1..20).to_a 
array.index.each_slice(5) do |slice| 
    puts slice.inspect 
    other_array.push(1) 
end 

возвращается только:

[1, 2, 3, 4, 5] 

Как other_array.push(1) ломает экс устранение блока? Очевидным выводом было бы то, что я не могу получить доступ к переменным, которые не входят в объем блока, но почему это?

+1

Фактически я не вижу смысла в вызове 'index' здесь (что является причиной, а не нажатием массива) – Vasfed

+1

В документации говорится, что' '' index''' «возвращает Enumerator, когда не задан ни блок, ни аргумент», поэтому я назвал '' 'index'''. Я, однако, нуждался в Enumerator, чтобы иметь возможность вызвать each_slice. Только теперь я вижу, что Array включает модуль Enumerable и, следовательно, сам метод each_slice. –

+0

Один из способов взглянуть на это: enum1 = array.index # => # ; enum2 = enum1.each_slice (5) # => # : each_slice (5)> '. Внимательно изучите это возвращаемое значение. Вы можете думать о 'enum2' как о« составном »перечислителе. Мы можем видеть элементы, генерируемые 'enum2', путем преобразования его в массив:' enum2.to_a # => [[1, ... 5], ... [16, ... 20]] '. Затем 'enum2.each {| slice | puts slice.inspect} # => [1, ... 5] ... [16, ... 20] '. Как вы заметили, вы получаете тот же результат, если '.index' опущен. –

ответ

6

Я нашел решение в документации по массиву. Я задавался вопросом, почему вы использовали функцию index для массива, когда кажется, что вы просто хотите перебирать массив. Для этого вы можете использовать array.each_slice без указания индекса. Индекс говорит следующее: http://ruby-doc.org/core-2.2.0/Array.html#method-i-index

Возвращает индекс первого объекта в арном таким образом, что объект является == в OBJ.

Если вместо аргумента задан блок, возвращается индекс первого объекта, для которого блок возвращает true. Возвращает nil, если совпадение не найдено

Таким образом, ваш код оценивает блок и проверяет, равен ли результат true. В первом примере вы делаете только puts, который возвращает nil. Ниль ошибочен.
Второй пример возвращает объект массива, содержащего единицу 1.
В рубине каждое условие равно true, если это не false или nil. Вы можете увидеть это здесь:

if nil 
    puts "foo" 
end 
=> nil 

other_array = [1] 
if other_array 
    puts "foo" 
end 
=> "foo" 

Так блок в вашем втором примере возвращает что-то не-ложные, поэтому он не будет работать снова, потому что он нашел «правильный» результат.

Для возврата вам может потребоваться знать, что ruby ​​возвращает последнее выражение в любой области, если иное не указано. Поэтому он возвращает other_array. Если вы не хотите, чтобы переформатировать код мог на следующее:

other_array = [] 
array = (1..20).to_a 
array.index.each_slice(5) do |slice| 
    puts slice.inspect 
    other_array.push(1) 
    nil 
end 

Это заставит блок вернуть nil и итерация будет работать.

+0

Это больше похоже на ошибку. Блок связан с 'each_slice (5)', он не должен влиять на поведение вызова 'index'. Кроме того, если блок ограничен вызовом 'index', он должен возвращать' 1', который является первым элементом, а вызов 'each_slice' на' 1' должен приводить к ошибке. –

+0

О да, вы правы. Я этого не видел. Странно, почему 'index.each_slice' фактически работает. index должен возвращать массив для этого. – 0xAffe

+1

В документации говорится, что '' 'index'''" возвращает Enumerator, когда не указан ни блок, ни аргумент ", поэтому я назвал' '' index'''. Я, однако, нуждался в Enumerator, чтобы иметь возможность вызвать each_slice. Только теперь я вижу, что Array включает модуль Enumerable и, следовательно, сам метод each_slice. –

2

кажется сложным на первый взгляд, но если вы проверяете документы для Array#index вы увидите, что этот метод возврата Enum если нет блока не дано.

Тогда вы называете #each_slice(n) на этом Enum объекта с блоком:

в первом случае:

do |slice| 
    puts slice.inspect 
end 

возвращает nil каждый раз #index метода. Вы получите тот же результат, если вы звоните array.index.each_slice(5) { nil } или

во втором:

do |slice| 
    puts slice.inspect 
    other_array.push(1) 
end 

значения этого блока получения оценивается внутри блока и возвращает [1] методы #index, поэтому он возвращает первый срез массива. Вы получите тот же результат, если вы вызываете array.index.each_slice(5) { any_non_falsy_object }