Почему рубиновые петли были разработаны таким образом? - PullRequest
1 голос
/ 22 февраля 2011

Как указано в заголовке, мне было любопытно узнать, почему Ruby решил отказаться от классического цикла for и вместо этого использовать array.each do ...

. Лично я считаю его немного менее читабельным, но это простомое личное мнение.Не нужно спорить об этом.С другой стороны, я полагаю, что они разработали его специально, для этого должна быть веская причина.

Итак, каковы преимущества такого размещения петель?В чем смысл этого дизайнерского решения?

Ответы [ 7 ]

7 голосов
/ 22 февраля 2011

Это дизайнерское решение является прекрасным примером того, как Ruby объединяет парадигмы объектно-ориентированного и функционального программирования. Это очень мощная функция, которая может создавать простой читаемый код.

Это помогает понять, что происходит. Когда вы запускаете:

array.each do |el|
  #some code
end

вы вызываете метод each объекта array, который, если верить имени переменной, является экземпляром класса Array. Вы передаете блок кода этому методу (блок эквивалентен функции). Затем метод может оценить этот блок и передать аргументы, используя block.call(args) или yield args. each просто перебирает массив и для каждого элемента вызывает блок, который вы передали с этим элементом в качестве аргумента.

Если бы each был единственным методом использования блоков, это было бы не так полезно, как многие другие методы, и вы даже можете создать свой собственный. Например, в массивах есть несколько методов итераторов, включая map, который делает то же самое, что и каждый, но возвращает новый массив, содержащий возвращаемые значения блока, и select, который возвращает новый массив, который содержит только элементы старого массива. для которого блок возвращает истинное значение. Подобные вещи было бы утомительно делать с использованием традиционных методов зацикливания.

Вот пример того, как вы можете создать свой собственный метод с блоком. Давайте создадим метод every, который будет немного похож на карту, но только для каждого n элементов в массиве.

class Array #extending the built in Array class
  def every n, &block #&block causes the block that is passed in to be stored in the 'block' variable. If no block is passed in, block is set to nil
    i = 0
    arr = []
    while i < self.length
      arr << (   block.nil? ?  self[i] : block.call(self[i])   )#use the plain value if no block is given
      i += n
    end
    arr
  end
end

Этот код позволит нам выполнить следующее:

[1,2,3,4,5,6,7,8].every(2) #= [1,3,5,7]   #called without a block
[1,2,3,4,5,6,7,8,9,10].every(3) {|el| el + 1 } #= [2,5,8,11]   #called with a block

Блоки допускают выразительный синтаксис (часто называемый внутренними DSL), например, веб-микрофрейм Sinatra. Синатра использует методы с блоками для краткого определения http-взаимодействия. например.

get '/account/:account' do |account|
  #code to serve of a page for this account
end

Этой простоты было бы трудно достичь без блоков Руби.

Надеюсь, это позволило вам увидеть, насколько мощна эта языковая функция.

2 голосов
/ 22 февраля 2011

Я думаю, что это было главным образом потому, что Матц интересовался изучением того, как будет выглядеть полностью объектно-ориентированный язык сценариев при его создании; эта функция в значительной степени основана на итераторах языка программирования CLU .

Оказалось, предоставить некоторые интересные преимущества; класс, который предоставляет метод each, может «смешиваться» с модулем Enumerable, чтобы предоставлять клиентам огромное разнообразие готовых итерационных процедур, что уменьшает количество утомительного массива / итерационный код list / hash / etc, который должен быть написан. (Когда-либо видели Java 4 и более ранние итераторы ?)

1 голос
/ 22 февраля 2011

Я думаю, что вы как бы предвзяты, когда задаете этот вопрос. Кто-то может спросить: «Почему петли C for спроектированы таким образом?». Подумайте об этом - зачем мне нужно вводить переменную счетчика, если я хочу перебирать только элементы массива? Скажем, сравните эти два (оба в псевдокоде):

for (i = 0; i < len(array); i++) {
  elem = array[i];
  println(elem);
}

и

for (elem in array) {
  println(elem);
}

Почему первый чувствовал бы себя более естественно, чем второй, за исключением исторических (почти социологических) причин?

И Ruby, как объектно-ориентированный как таковой, делает это еще дальше, превращая его в метод массива:

array.each do |elem|
  puts elem
end

Приняв это решение, Матц просто сделал язык легче для избыточной синтаксической конструкции (цикл foreach), делегировав ее использование обычным методам и блокам (замыканиям). Я ценю Ruby больше всего именно по этой причине - он действительно рациональн и экономичен с языковыми особенностями, но сохраняет выразительность.

Я знаю, я знаю, у нас есть for в Ruby, но большинство людей считают это ненужным.

0 голосов
/ 23 февраля 2011

Это происходит из Smalltalk, который реализует управляющие структуры как методы, тем самым уменьшая количество ключевых слов и упрощая анализатор.Таким образом, позволяя управляющим структурам служить основой концепции для определения языка.

В ST, даже если условия являются методами, в моде:

boolean.ifTrue ->{executeIfBody()}, :else=>-> {executeElseBody()}

В конце концов, если вы игнорируетеВаш культурный уклон, то, что будет легче разобрать для машины, также будет легче разобрать самостоятельно.

0 голосов
/ 22 февраля 2011

Я думаю, это потому, что он подчеркивает философию «все является объектом», стоящую за Ruby: метод each вызывается для объекта.

Тогда переключение на другой итератор гораздо более плавное, чем изменение логикинапример, петля for.

0 голосов
/ 22 февраля 2011

Блоки do ... end (или { ... }) образуют так называемый блок (почти замыкание, IIRC).Думайте о блоке как о анонимном методе, который вы можете передать как аргумент другому методу.Блоки часто используются в Ruby, и поэтому эта форма итерации естественна: блок do ... end передается в качестве аргумента методу each.Теперь вы можете записать различные варианты в each, например, для итерации в обратном порядке или еще много чего.

Также существует синтаксическая сахарная форма:

for element in array
   # Do stuff
end

Блоки также используются, например, для фильтрациимассив:

array = (1..10).to_a
even = array.select do |element|
    element % 2 == 0
end
# "even" now contains [2, 4, 6, 8, 10]
0 голосов
/ 22 февраля 2011

Руби был спроектирован, чтобы быть выразительным, читать так, как если бы о нем говорили ... Тогда я думаю, что он просто эволюционировал оттуда.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...