Чому 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і встановити його, чому б не зробити те ж саме для ++?

Чи узгодженість завдань із =символом є єдиною причиною цього, чи я щось пропускаю?


2
Греп рубін вихідний код для таких операторів. Якщо таких немає - Матц не любить їх.
Еймантас

Ви не можете робити попередню роботу з +=оператором. У CI намагайтеся використовувати ++/ --тільки всередині умовних умов, віддаючи перевагу більш буквальному +=/ -=в базовому твердженні. Можливо, тому, що я навчився Python (довго після C, хоча ...)
Nick T

Чи не було подібного питання для Python тільки вчора?
BoltClock

@Eimantas, очевидно, авторам мови вони не подобалися. Це занадто часто, щоб не помітити. Мені було цікаво ЧОМУ, що дещо було прояснено відповідями нижче.
Andy_Vulhop

1
Я думаю, що це (майже) модельне питання. Отримати розглянутий відповідь - це не те, що легко знайти в Google. Цілком чітко і конкретно в тому, яка відповідь потрібна, і відповідь проливає світло на грані програмування, що може змусити задуматися ширше, ніж просто ядро ​​питання.
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 - це так само, як wack, і Ruby просто викликає помилку, коли ви це робите. Так що 1 ++ не важче впоратися. Можливо, потреба парсера впоратися з таким видом синтаксичного цукру небажана.
Стів Мідглі

28

Однією з причин є те, що до цього часу кожен оператор призначення (тобто оператор, який змінює змінну) має =в ньому. Якщо додати ++і --, це вже не так.

Ще одна причина полягає в тому, що поведінка ++і --часто бентежить людей. Справа в точці: повернене значення i++у вашому прикладі фактично було б 1, а не 2 ( iоднак нове значення було б 2).


4
Більше, ніж будь-яка інша причина поки що, раціональне, що "всі завдання мають своє =", здається, має сенс. Я можу так поважати це, як жорстоке дотримання послідовності.
Andy_Vulhop

як щодо цього: a.capitalize! (неявне присвоєння а)
Луїс Соарес

1
@ LuísSoares a.capitalize!не перепризначає a, він буде вимкнути рядок, на який aпосилається. Інші посилання на ту саму рядок будуть впливати, і якщо ви будете робити a.object_idдо і після дзвінка до capitalize, ви отримаєте той самий результат (жоден з них не був би вірним, якби ви зробили це a = a.capitalizeзамість).
sepp2k

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

1
Ого. Це страшно ... Знаючи, що в Java рядки незмінні .. Але з владою приходить відповідальність. Дякую за пояснення.
Luís Soares

25

Це не є звичайним для мов ОО. Насправді, ++у Smalltalk немає мови, яка ввела термін "об'єктно-орієнтоване програмування" (і на мову Ruby найбільш сильно впливає). Що ви маєте на увазі, це те, що це звичайно в мові С та мовах, що імітують C. Рубі має синтаксис, схожий на С, але він не є рабським у дотриманні традицій С.

Щодо того, чому його немає в Рубі: Матц цього не хотів. Це дійсно кінцева причина.

Причина відсутності подібного в Smalltalk полягає в тому, що це частина переважаючої філософії мови, що призначення змінної є принципово іншим видом , ніж надсилання повідомлення об'єкту - це на іншому рівні. Таке мислення, ймовірно, вплинуло на Маца при розробці Рубі.

Включити його в Ruby було б неможливо - ви могли легко написати препроцесора, який перетворює все ++на +=1. але, очевидно, Матсу не сподобалася ідея оператора, який зробив "приховане завдання". Дещо дивно здається, що оператор із прихованим цілим операндом всередині нього. Жоден інший оператор мови не працює таким чином.


1
Я не думаю, що пропозиція препроцесора спрацює; (не експерт), але я думаю, що i = 42, i ++ повернеться 42, де i + = 1 повернеться 43. Чи я неправильний у цьому? Тож вашою пропозицією в такому випадку буде використання i ++ як ++, як правило, використовується, що є досить поганим імхо і може принести більше шкоди, ніж користі.
AturSams

12

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

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

Насправді, наскільки я бачив, більшу частину часу +=1використовують люди, щойно переселилися на Рубі з мов С-стилю.

Коротше кажучи, це дійсно під питанням , якщо методи ++і --будуть використовуватися на всіх.


1
Це найкраща відповідь імхо. ++ часто використовується для ітерації. Рубін не заохочує такого типу ітерацій.
AturSams

3

Я думаю, що міркування Маца про те, що їм не подобається, полягає в тому, що він фактично замінює змінну новою.

колишній:

a = SomeClass.new
def a.go
  'Здравствуйте'
кінець
# у цей момент ви можете зателефонувати на a.go
# але якщо ви зробили ++
# це насправді означає a = a + 1
# тому ви більше не можете телефонувати на a.go
# як ви втратили оригінал

Тепер, якщо хтось міг переконати його, що він повинен просто зателефонувати #succ! чи що ні, це мало б більше сенсу і уникне проблеми. Можна запропонувати його на рубіновому ядрі.


9
"Ви можете запропонувати це на рубіновому ядрі" ... Після того, як ви прочитали та зрозуміли аргументи у всіх інших потоках, де це було запропоновано востаннє, і час до цього, і час до цього, і час до цього, і час до цього, і ... Я не дуже довго був у спільноті Рубі, але якраз під час свого часу я пам’ятаю щонайменше двадцять таких дискусій.
Йорг W Міттаг

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 ".


2

І словами Девіда Блека з його книги "Добре заземлений рубіст":

Деякі об'єкти в Ruby зберігаються в змінних як безпосередні значення. До них відносяться цілі числа, символи (які виглядають так: це) та спеціальні об'єкти true, false та nil. Коли ви присвоюєте одне з цих значень змінній (x = 1), змінна містить саме значення, а не посилання на нього. На практиці це не має значення (і це часто залишатиметься як мається на увазі, а не повторюється в обговоренні посилань та суміжних тем у цій книзі). Ruby обробляє перенаправлення посилань на об'єкти автоматично; не потрібно робити зайвих робіт, щоб надсилати повідомлення об’єкту, який містить, скажімо, посилання на рядок, на відміну від об'єкта, який містить безпосереднє ціле значення. Але правило представлення негайних цінностей має кілька цікавих наслідків, особливо якщо мова йде про цілі числа. З одного боку, будь-який об'єкт, який представляється як безпосереднє значення, завжди є точно тим самим об'єктом, незалежно від того, скільки змінних йому призначено. Є лише один об'єкт 100, лише один об'єкт помилковий тощо. Безпосередня, унікальна природа цілочисленних змінних полягає в тому, що Рубі не вистачає операторів до і після збільшення, тобто, в Ruby цього не можна робити: x = 1 x ++ # Немає такого оператора. Причина тому до негайної присутності 1 в x, x ++ було б як 1 ++, це означає, що ви будете змінювати число 1 на число 2 - і це не має сенсу. незалежно від того, скільки змінних йому призначено. Є лише один об'єкт 100, лише один об'єкт помилковий тощо. Безпосередня, унікальна природа цілочисленних змінних полягає в тому, що Рубі не вистачає операторів до і після збільшення, тобто, в Ruby цього не можна робити: x = 1 x ++ # Немає такого оператора. Причина тому до негайної присутності 1 в x, x ++ було б як 1 ++, це означає, що ви змінили б число 1 на число 2 - і це не має сенсу. незалежно від того, скільки змінних йому призначено. Є лише один об'єкт 100, лише один об'єкт помилковий тощо. Безпосередня, унікальна природа цілочисленних змінних полягає в тому, що Рубі не вистачає операторів до і після збільшення, тобто, в Ruby цього не можна робити: x = 1 x ++ # Немає такого оператора. Причина тому до негайної присутності 1 в x, x ++ було б як 1 ++, це означає, що ви змінили б число 1 на число 2 - і це не має сенсу.


Але як ви тоді можете зробити "1. наступний"?
Магне

1

Не вдалося цього досягти, додавши новий метод до класу fixnum або Integer?

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

повертає 2

"Деструктивні" методи, здається, додаються, !щоб попередити можливих користувачів, тому додавання нового методу, який називається next!, в значній мірі зробить те, що було запропоновано, тобто.

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

повертає 2 (з моменту збільшення нуля)

Звичайно, next!метод повинен був би перевірити, що об'єкт був цілою змінною, а не дійсне число, але це повинно бути доступним.


1
Integer#nextвже існує (більш-менш), за винятком того, що він називається Integer#succзамість (для "наступника"). Але Integer#next!(або Integer#succ!) було б нісенітницею: пам’ятайте, що методи працюють на об'єктах , а не на змінних , тому numb.next!було б рівно рівним 1.next!, тобто, було б мутувати 1, що дорівнює 2 . ++було б незначно краще, оскільки це може бути синтаксичним цукром для завдання, але особисто я віддаю перевагу поточному синтаксису, де виконуються всі завдання =.
філоморія

Щоб завершити коментар вище: та Integer#predотримати попередника.
Йоні

-6

Перевірте цих операторів із сімейства C у irb і перевірте їх на собі:

x = 2    # x is 2
x += 2   # x is 4
x++      # x is now 8
++x      # x reverse to 4

3
Це явно неправильно і не працює, як (x++)і недійсне твердження в Ruby.
Anothermh
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.