Почему Ruby не поддерживает i ++ или i-- (операторы увеличения / уменьшения)?

130

Операторы увеличения / уменьшения до / после операции ( ++и --) представляют собой довольно стандартный синтаксис языка программирования (по крайней мере, для процедурных и объектно-ориентированных языков).

Почему Руби их не поддерживает? Я понимаю, что вы могли бы сделать то же самое с помощью +=и -=, но кажется странным произвольным исключать что-то подобное, тем более что это настолько кратко и условно.

Пример:

i = 0    #=> 0
i += 1   #=> 1
i        #=> 1
i++      #=> expect 2, but as far as I can tell, 
         #=> irb ignores the second + and waits for a second number to add to i

Я понимаю, что Fixnumэто неизменяемое, но если +=можно просто создать новый экземпляр Fixnumи установить его, почему бы не сделать то же самое для ++?

=Единственная причина этого - последовательность в назначениях, содержащих персонажа, или я что-то упускаю?

Andy_Vulhop
источник
2
Исходный код grep ruby ​​для таких операторов. Если их нет - они не нравятся Мацу.
Eimantas
Вы не можете выполнить преинкремент с помощью +=оператора. В C я стараюсь использовать ++/ --только внутри условных выражений , предпочитая более буквальное +=/ -=в базовом выражении. Вероятно, потому что я выучил Python (задолго до C, хотя ...)
Ник Т.
Разве вчера не было такого вопроса для Python?
BoltClock
@Eimant: ну, очевидно, создатели языка не любили их. Это слишком часто, чтобы не заметить. Мне было интересно, ПОЧЕМУ, что несколько поясняется ответами ниже.
Andy_Vulhop
1
Я думаю, что это (почти) модельный вопрос SO. Это не то, что нелегко погуглить, чтобы получить взвешенный ответ. Совершенно ясно и конкретно, какой ответ требуется, и этот ответ проливает свет на тот аспект программирования, который может заставить человека мыслить шире, чем просто суть вопроса.
PurplePilot

Ответы:

97

Вот как Мац (Юкихиро Мацумото) объясняет это в старой беседе :

Hi,

In message "[ruby-talk:02706] X++?"
    on 00/05/10, Aleksi Niemelä <aleksi.niemela@cinnober.com> writes:

|I got an idea from http://www.pragprog.com:8080/rubyfaq/rubyfaq-5.html#ss5.3
|and thought to try. I didn't manage to make "auto(in|de)crement" working so
|could somebody help here? Does this contain some errors or is the idea
|wrong?

  (1) ++ and -- are NOT reserved operator in Ruby.

  (2) C's increment/decrement operators are in fact hidden assignment.
      They affect variables, not objects.  You cannot accomplish
      assignment via method.  Ruby uses +=/-= operator instead.

  (3) self cannot be a target of assignment.  In addition, altering
      the value of integer 1 might cause severe confusion throughout
      the program.

                            matz.
Брайан
источник
10
2 и 3 кажутся противоречивыми. Если самостоятельное назначение плохое, почему +=/ -=нормально? И не было 1+=1бы так плохо? (Он терпит неудачу в IRB с syntax error, unexpected ASSIGNMENT)
Andy_Vulhop
2
(2) означает, что в C вы не изменяете само значение ... вы изменяете содержимое переменной, которая содержит значение. Это слишком мета для любого языка, который передается по значению. Если в Ruby нет способа передать что-либо по ссылке (я имею в виду действительно «по ссылке», а не по передаче ссылки по значению), изменение самой переменной было бы невозможно в рамках метода.
cHao
5
Может, я что-то здесь упускаю. +=заменяет объект, на который ссылается переменная, на совершенно новый объект. Вы можете проверить это, позвонив i.object_idдо и после i+=1. Почему это было технически сложнее ++?
Andy_Vulhop
6
@Andy_Vulhop: №3 объясняет, почему технически невозможно, чтобы назначение было методом, а не почему назначение невозможно вообще (постер Матц отвечал на мысль, что возможно создать ++метод).
Чак
2
В Ruby все литералы также являются объектами. Поэтому я считаю, что Матц пытается сказать, что он не уверен, что ему нравится идея работы с 1 ++ в качестве утверждения. Лично я считаю, что это неразумно, поскольку, как говорит @Andy_Vulhop, 1 + = 2 - это так же глупо, а Ruby просто вызывает ошибку, когда вы это делаете. Так что с 1 ++ справиться не сложнее. Возможно, необходимость парсера справляться с таким синтаксическим сахаром нежелательна.
Стив Мидгли
28

Одна из причин заключается в том, что до сих пор каждый оператор присваивания (т.е. оператор, изменяющий переменную) имел =в себе. Если вы добавите ++и --, это уже не так.

Другая причина в том, что поведение ++и --часто сбивает с толку людей. Показательный пример: возвращаемое значение i++в вашем примере на самом деле будет 1, а не 2 ( iоднако новое значение будет 2).

sepp2k
источник
4
Более, чем какая-либо другая причина до сих пор, =кажется, имеет смысл рассуждение о том, что «все задания содержат в себе». Я могу относиться к этому с уважением как к неистовой приверженности последовательности.
Andy_Vulhop
как насчет этого: a.capitalize! (неявное присвоение a)
Луис Соарес
1
@ LuísSoares a.capitalize!не переназначает a, он изменяет строку, на которую aуказывает. Другие ссылки на ту же строку будут затронуты, и если вы сделаете это a.object_idдо и после вызова capitalize, вы получите тот же результат (ни один из которых не был бы верным, если бы вы это сделали a = a.capitalize).
sepp2k 06
1
@ LuísSoares Как я уже сказал, a.capitalize!повлияет на другие ссылки на ту же строку. Это очень практическая разница. Например, если у вас есть, def yell_at(name) name.capitalize!; puts "HEY, #{name}!" endа затем вы назовете его так:, my_name = "luis"; yell_at(my_name)значение my_nameбудет теперь равным "LUIS", тогда как оно не изменилось бы, если бы вы использовали capitalizeи присвоение.
sepp2k 06
1
Вот это да. Это страшно ... Зная, что в Java строки неизменяемы ... Но сила приходит с ответственностью. Спасибо за объяснение.
Луис Соарес
25

Это необычно для объектно-ориентированных языков. Фактически, нет ++в Smalltalk, языке, который ввел термин «объектно-ориентированное программирование» (а язык Ruby находится под сильным влиянием). Вы имеете в виду, что это обычное явление для C и языков, сильно имитирующих C. Ruby действительно имеет синтаксис, отчасти похожий на C, но он не рабски придерживается традиций C.

Что касается того, почему этого нет в Ruby: Мац этого не хотел. Это действительно главная причина.

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

Было бы невозможно включить его в Ruby - вы могли бы легко написать препроцессор, который все преобразует ++в +=1. но, очевидно, Матцу не понравилась идея оператора, выполняющего «скрытое назначение». Также кажется немного странным наличие оператора со скрытым целым операндом внутри него. Ни один другой оператор в языке не работает таким образом.

цыпленок
источник
1
Я не думаю, что ваше предложение препроцессора сработает; (не эксперт), но я думаю, что i = 42, i ++ вернет 42, где i + = 1 вернет 43. Я ошибаюсь в этом? Итак, ваше предложение в этом случае было бы использовать i ++, поскольку обычно используется ++ i, что довольно плохо imho и может принести больше вреда, чем пользы.
AturSams
12

Я думаю, есть еще одна причина: ++в Ruby не будет удаленной пользы, как в C и его прямых преемниках.

Причина в том, что forключевое слово: хотя это важно в C, в Ruby оно по большей части излишне. Большая часть итераций в Ruby выполняется с помощью методов Enumerable, таких как eachи mapпри итерации по некоторой структуре данных, и Fixnum#timesметода, когда вам нужно выполнить цикл определенное количество раз.

На самом деле, насколько я видел, большую часть времени +=1используют люди, только что перешедшие на Ruby с языков C-стиля.

Короче говоря, действительно сомнительно, будут ли методы ++и --вообще использоваться.

Младен Ябланович
источник
1
Это лучший ответ имхо. ++ часто используется для итерации. Ruby не поддерживает этот тип итераций.
AturSams
3

Я думаю, что Матц не любит их потому, что фактически заменяет переменную на новую.

например:

a = SomeClass.new
def a.go
  'Привет'
конец
# на этом этапе вы можете вызвать a.go
# но если вы сделали a ++
# что на самом деле означает a = a + 1
# так что вы больше не можете называть a.go
# так как вы потеряли оригинал

Теперь, если бы кто-нибудь мог убедить его, что он должен просто позвонить #succ! или что нет, это имело бы больший смысл и позволило бы избежать проблемы. Можете предложить на рубиновом ядре.

rogerdpack
источник
9
"Вы можете предложить это на ядре Ruby" ... После того, как вы прочитали и поняли аргументы во всех других потоках, где это было предложено в последний раз, и время до этого, и время до этого, и время до этого, и время до этого, и ... Я не очень давно был в сообществе Ruby, но только за время своего существования я помню по крайней мере двадцать таких обсуждений.
Jörg W Mittag
3

Вы можете определить .+оператор самоприращения:

class Variable
  def initialize value = nil
    @value = value
  end
  attr_accessor :value
  def method_missing *args, &blk
    @value.send(*args, &blk)
  end
  def to_s
    @value.to_s
  end

  # pre-increment ".+" when x not present
  def +(x = nil)
    x ? @value + x : @value += 1
  end
  def -(x = nil)
    x ? @value - x : @value -= 1
  end
end

i = Variable.new 5
puts i                #=> 5

# normal use of +
puts i + 4            #=> 9
puts i                #=> 5

# incrementing
puts i.+              #=> 6
puts i                #=> 6

Дополнительная информация о «переменной класса» доступна в разделе « Переменная класса для увеличения объектов Fixnum ».

Sony Santos
источник
2

Как сказал Дэвид Блэк из своей книги «Основанный на рубине»:

Некоторые объекты в Ruby хранятся в переменных как непосредственные значения. К ним относятся целые числа, символы (которые выглядят так: this) и специальные объекты true, false и nil. Когда вы назначаете одно из этих значений переменной (x = 1), переменная содержит само значение, а не ссылку на него. С практической точки зрения это не имеет значения (и это часто будет оставлено подразумеваемым, а не повторяться повторно при обсуждении ссылок и связанных тем в этой книге). Ruby автоматически выполняет разыменование объектных ссылок; вам не нужно выполнять дополнительную работу, чтобы отправить сообщение объекту, который содержит, скажем, ссылку на строку, в отличие от объекта, который содержит непосредственное целочисленное значение. Но у правила представления непосредственной ценности есть несколько интересных ответвлений: особенно когда дело касается целых чисел. Во-первых, любой объект, представленный как непосредственное значение, всегда является одним и тем же объектом, независимо от того, скольким переменным он назначен. Есть только один объект 100, только один объект false и так далее. Непосредственный, уникальный характер переменных с целочисленной привязкой стоит за отсутствием в Ruby операторов пре- и пост-инкремента, то есть в Ruby вы не можете этого сделать: x = 1 x ++ # Нет такого оператора Причина в том, что из-за для непосредственного присутствия 1 в x, x ++ будет подобен 1 ++, что означает, что вы измените число 1 на число 2, а это не имеет смысла. независимо от того, скольким переменным он назначен. Есть только один объект 100, только один объект false и так далее. Непосредственный, уникальный характер переменных с целочисленной привязкой стоит за отсутствием в Ruby операторов пре- и пост-инкремента, то есть в Ruby вы не можете этого сделать: x = 1 x ++ # Нет такого оператора Причина в том, что из-за для непосредственного присутствия 1 в x, x ++ будет подобен 1 ++, что означает, что вы измените число 1 на число 2, а это не имеет смысла. независимо от того, скольким переменным он назначен. Есть только один объект 100, только один объект false и так далее. Непосредственный, уникальный характер переменных с целочисленной привязкой стоит за отсутствием в Ruby операторов пре- и пост-инкремента, то есть в Ruby вы не можете этого сделать: x = 1 x ++ # Нет такого оператора Причина в том, что из-за для непосредственного присутствия 1 в x, x ++ будет подобен 1 ++, что означает, что вы измените число 1 на число 2, а это не имеет смысла.

Александр Суонн
источник
Но как же тогда вы можете сделать "1.next"?
Magne
1

Нельзя ли этого добиться, добавив новый метод в класс fixnum или Integer?

$ ruby -e 'numb=1;puts numb.next'

возвращает 2

Кажется, что "деструктивные" методы добавляются !для предупреждения возможных пользователей, поэтому добавление нового вызываемого метода в next!значительной степени сделает то, что было запрошено, т.е.

$ ruby -e 'numb=1; numb.next!; puts numb' 

возвращает 2 (поскольку число numb было увеличено)

Конечно, next!метод должен будет проверить, является ли объект целочисленной переменной, а не действительным числом, но это должно быть доступно.

Sjerek
источник
1
Integer#nextуже существует (более или менее), за исключением того, что он называется Integer#succвместо этого (для «преемника»). Но Integer#next!(или Integer#succ!) было бы чепухой: помните, что методы работают с объектами , а не с переменными , поэтому numb.next!были бы точно равны 1.next!, то есть изменили бы 1 на 2 . ++было бы немного лучше, поскольку это может быть синтаксический сахар для присваивания, но лично я предпочитаю текущий синтаксис, в котором все присваивания выполняются с =.
Филомория
Чтобы завершить комментарий выше: и Integer#predполучить предшественника.
Йони
-6

Проверьте эти операторы из семейства C в irb Ruby и проверьте их сами:

x = 2    # x is 2
x += 2   # x is 4
x++      # x is now 8
++x      # x reverse to 4
Аунг Зан Бау
источник
3
Это явно неверно и не работает, как (x++)недопустимый оператор в Ruby.
anothermh