ОП написал
Мне кажется странным, что приведенная выше конструкция не дает ожидаемого результата. В чем причина этого? В каких ситуациях такое поведение разумно?
а не «Можно ли это сделать?» но чтобы ответить на вопрос, который не был задан, прежде чем перейти к вопросу, который был задан на самом деле:
$ irb
2.1.5 :001 > (0..4)
=> 0..4
2.1.5 :002 > (0..4).each { |i| puts i }
0
1
2
3
4
=> 0..4
2.1.5 :003 > (4..0).each { |i| puts i }
=> 4..0
2.1.5 :007 > (0..4).reverse_each { |i| puts i }
4
3
2
1
0
=> 0..4
2.1.5 :009 > 4.downto(0).each { |i| puts i }
4
3
2
1
0
=> 4
Поскольку заявляется, что reverse_each строит весь массив, очевидно, что метод downo будет более эффективным. Тот факт, что разработчик языка может даже подумать о реализации таких вещей, как бы связан с ответом на фактический вопрос, который задается.
Чтобы ответить на вопрос, как на самом деле ...
Причина в том, что Ruby - бесконечно удивительный язык. Некоторые сюрпризы приятны, но есть много поведения, которое совершенно нарушено. Даже если некоторые из этих следующих примеров будут исправлены в новых версиях, есть много других, и они остаются обвинительными актами в отношении мировоззрения первоначального дизайна:
nil.to_s
.to_s
.inspect
приводит к "" но
nil.to_s
# .to_s # Don't want this one for now
.inspect
приводит к
syntax error, unexpected '.', expecting end-of-input
.inspect
^
Вы, вероятно, ожидаете, что << и push будут такими же для добавления в массивы, но
a = []
a << *[:A, :B] # is illegal but
a.push *[:A, :B] # isn't.
Вы, вероятно, ожидаете, что 'grep' будет вести себя как его эквивалент в командной строке Unix, но он соответствует === not = ~, несмотря на свое имя.
$ echo foo | grep .
foo
$ ruby -le 'p ["foo"].grep(".")'
[]
Различные методы неожиданно являются псевдонимами друг для друга, поэтому вам придется выучить несколько имен для одного и того же объекта - например, find
и detect
- даже если вам нравится большинство разработчиков и вы всегда используете только одно или другое. Во многом то же самое касается size
, count
и length
, за исключением классов, которые определяют каждый по-разному или не определяют один или два вообще.
Если кто-то не реализовал что-то еще - например, основной метод tap
был переопределен в различных библиотеках автоматизации, чтобы нажимать что-то на экране. Удачи в выяснении того, что происходит, особенно если какой-то модуль, требуемый другим модулем, обезвредил еще один модуль, чтобы сделать что-то недокументированное.
Объект переменной среды, ENV, не поддерживает «слияние», поэтому вам нужно написать
ENV.to_h.merge('a': '1')
В качестве бонуса вы даже можете переопределить свои или чужие константы, если передумаете, какими они должны быть.