Ruby — это еще один скриптоый язык, который в некотором роде похож на Python. В этой статье мы расскажем вам о том, как жва эти языка подходят к циклам for со своими парадигамами.
Как циклы устроены в Python
В Python оператор for
указывает объекту, как себя вести согласно протоколу (__ter__
и __next__
). В Ruby все наоборот. В нем оператор for
сам является методом объекта. Вызывающий сам передает тело с циклом в этот метод.
NLP с Python
Код курса
PNLP
Ближайшая дата курса
24 февраля, 2025
Продолжительность
40 ак.часов
Стоимость обучения
90 000 руб.
В основе Python лежит передача объекта в цикл for
. В Ruby цикл передается объекту. Поэтому в Python для того чтобы над объектом можно было итерироваться, объект должен сам задать, каким образом это делать. Это достигается за счет реализации магических методов __iter__
и __next__
:
class Stuff: def __init__(self): self.a_list = [1,2,3,4] self.position = 0 def __next__(self): try: value = self.a_list[self.position] self.position += 1 return value except IndexError: self.position = 0 raise StopIteration def __iter__(self): return self
Теперь объект этого класса можно передать в цикл:
for data in Stuff(): print(data)
Как циклы устроены в Ruby
В основе же Ruby лежит противоположная концепция. Разработчику нужно самому создать for
в виде метода, который принимает тело с самим циклом. Для этого нужно реализовать метод each
, который должен выдавать соответствующий элемент через yeild
. Иными словам, содержит целый блок я
Перепишем класс выше на Ruby:
class Stuff def initialize @a_list = [1, 2, 3, 4] end def each for item in @a_list yield item end end end
Теперь итерироваться нужно так:
Stuff.new().each do |item| puts item end
Здесь вместо того, чтобы передавать данные в цикл for
, вы передаете целый код в данные. На самом деле здесь заложена более глубокая мысль.
ООП — главенствующая парадигма Ruby
В Python все реализуется через протоколы, а __iter__
и __next__
только некоторые из них. Как известно, он имеет конструкции list comprehension, которые можно использовать только благодаря протоколу:
>>> [item for item in Stuff()] >>> [1, 2, 3, 4] >>> [item for item in Stuff() if item % 2 == 0] >>> [2, 4]
Ruby в этом плане идет дальше со своим подходом, где главенствует метод, а не объект. Вместо list comprehension применяются методы, которые обычно реализованы через так называемые коллекции:
class Stuff ... def select out = [] each do |e| # Если блок возвращает истину, то добавить к `out` if yield(e) out << e end end out end def map out = [] # Добавить выходное значение из `e` в `out` each {|e| out << yield(e) } out end
Теперь код может быть переписан так:
puts Stuff.new().map {|item| item} puts Stuff.new().select{|item| item.even?}
Python говорит: “ты определяешь, как итерирроваться над объектом, а я решу, что делать с его элементами”.
Объекты Ruby более настраиваемые. Конечно, можно просто добавить больше контроля внутри блоков. Конечно, можно сделать так, что each
будет делать то, что делает map
. Но Ruby предоставляет объектам иметь разные реализации map
и each
(возможно использование each
в виде map
будет неоптимально и небезопасно).
В Ruby объекты все контролируют, в Python — сам язык. В Python все строго регламентировано. Ruby же говорит: “могут появиться такие ситуации, при которых вызывающем не нужно давать полномочия в руки, сам реши, что делать”. Поэтому разработчик дает возможность объектам самими решать, что с ними делать. С этой стороны можно заявить, что Python живет в более процедурной парадигме, а Ruby живет в более объектно-ориентированном.
На наших курсах мы полностью ориентируемся на достоинства Python, которые делают разработку проектов в проще. Больше об этом вы узнаете на наших образовательных курсах в лицензированном учебном центре обучения и повышения квалификации руководителей и ИТ-специалистов (менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data) в Москве: