Operator inkrementacji / dekrementacji pre / post ( ++
i --
) jest dość standardową składnią języka programowania (przynajmniej dla języków proceduralnych i obiektowych).
Dlaczego Ruby ich nie obsługuje? Rozumiem, że możesz osiągnąć to samo za pomocą +=
i -=
, ale wykluczenie czegoś takiego wydaje się dziwnie arbitralne, zwłaszcza że jest tak zwięzłe i konwencjonalne.
Przykład:
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
Rozumiem, że Fixnum
jest niezmienny, ale jeśli +=
mogę po prostu zainicjować nowy Fixnum
i ustawić go, dlaczego nie zrobić tego samego dla ++
?
Czy konsekwencja w przypisaniach zawierających =
znak jest jedynym tego powodem, czy czegoś mi brakuje?
ruby
operators
language-design
Andy_Vulhop
źródło
źródło
+=
operatorem. W CI spróbuj używać++
/--
tylko wewnątrz warunków warunkowych, preferując bardziej dosłowne+=
/-=
w podstawowej instrukcji. Prawdopodobnie dlatego, że nauczyłem się Pythona (choć długo po C ...)Odpowiedzi:
Oto jak Matz (Yukihiro Matsumoto) wyjaśnia to w starym wątku :
źródło
+=
/-=
ok? I nie1+=1
byłoby tak źle? (Nie udaje się w IRB zsyntax error, unexpected ASSIGNMENT
)+=
zastępuje obiekt, do którego odwołuje się zmienna, zupełnie nowym obiektem. Możesz to sprawdzić dzwoniąci.object_id
przed i poi+=1
. Dlaczego miałoby to być bardziej skomplikowane technicznie++
?++
metody).Jednym z powodów jest to, że do tej pory każdy operator przypisania (tj. Operator zmieniający zmienną) ma w sobie znak
=
. Jeśli dodasz++
i--
, to już nie jest.Innym powodem jest to, że zachowanie
++
i--
często dezorientują ludzi. Przykład: wartość zwracanai++
w Twoim przykładzie będzie w rzeczywistości wynosić 1, a nie 2 (nowa wartośći
będzie jednak wynosić 2).źródło
=
w sobie swoje”, wydaje się mieć sens. Mogę to trochę szanować jako zaciekłe trzymanie się konsekwencji.a.capitalize!
nie zmienia przypisaniaa
, zmieni ciąg,a
do którego się odnosi. Wpłynie to na inne odwołania do tego samego ciągu, a jeśli zrobisz toa.object_id
przed i po wywołaniu funkcjicapitalize
, otrzymasz ten sam wynik (z których żaden nie byłby prawdziwy, gdybyś to zrobiła = a.capitalize
).a.capitalize!
wpłynie to na inne odwołania do tego samego ciągu. To bardzo praktyczna różnica. Na przykład, jeśli masz,def yell_at(name) name.capitalize!; puts "HEY, #{name}!" end
a następnie nazwiesz to w tenmy_name = "luis"; yell_at(my_name)
sposób:, wartośćmy_name
will będzie teraz wynosić"LUIS"
, podczas gdy nie zmieni się to, jeśli użyłeścapitalize
i przypisanie.To nie jest konwencjonalne w językach OO. W rzeczywistości nie ma
++
w Smalltalk, języku, który ukuł termin „programowanie obiektowe” (a język Ruby jest pod największym wpływem). Masz na myśli to, że jest konwencjonalny w C, a języki ściśle imitują C. Ruby ma składnię podobną do C, ale nie jest niewolniczy w trzymaniu się tradycji C.Co do tego, dlaczego nie ma go w Ruby: Matz tego nie chciał. To naprawdę ostateczny powód.
Powodem ma czegoś takiego istnieje w Smalltalk to dlatego, że jest częścią języka jest nadrzędne filozofię, że przypisanie zmiennej jest zasadniczo inny rodzaj rzeczy niż wysyłanie wiadomości do obiektu - jest na innym poziomie. To myślenie prawdopodobnie wpłynęło na Matza podczas projektowania Rubiego.
Nie byłoby niemożliwe włączenie go do Rubiego - można łatwo napisać preprocesor, który przekształci wszystko
++
w+=1
. ale najwyraźniej Matzowi nie podobał się pomysł operatora, który wykonywał „ukryte zadanie”. Wydaje się również trochę dziwne, aby mieć operator z ukrytym operandem całkowitym w środku. Żaden inny operator w tym języku nie działa w ten sposób.źródło
Myślę, że jest jeszcze jeden powód:
++
w Rubim nie byłoby to nawet przydatne, jak w C i jego bezpośrednich następcach.Powód jest taki, że
for
słowo kluczowe: chociaż jest niezbędne w C, w Rubim jest przeważnie zbyteczne. Większość iteracji w Rubim odbywa się za pomocą metod Enumerable, takich jakeach
imap
podczas iteracji przez pewną strukturę danych orazFixnum#times
metody, gdy trzeba wykonać pętlę dokładną liczbę razy.Właściwie, o ile widziałem, większość czasu
+=1
jest używana przez osoby, które niedawno przeszły na Ruby z języków w stylu C.Krótko mówiąc, naprawdę wątpliwe jest, czy metody
++
i--
czy w ogóle byłyby używane.źródło
Myślę, że uzasadnienie Matza, dlaczego ich nie lubi, jest takie, że faktycznie zastępuje zmienną nową.
dawny:
Teraz gdyby ktoś mógł go przekonać, że powinien po prostu zadzwonić na #succ! a co nie, to miałoby większy sens i pozwoliło uniknąć problemu. Możesz zasugerować to na rubinowym rdzeniu.
źródło
Możesz zdefiniować
.+
operator autoinkrementacji: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
Więcej informacji na temat „zmiennej klasy” można znaleźć w sekcji „ Zmienna klasy w celu zwiększenia liczby obiektów o stałej liczbie ”.
źródło
I słowami Davida Blacka z jego książki „The Well-Grounded Rubyist”:
źródło
Czy nie można tego osiągnąć, dodając nową metodę do klasy fixnum lub Integer?
$ ruby -e 'numb=1;puts numb.next'
zwraca 2
Wydaje się, że metody „destrukcyjne” są dołączane w
!
celu ostrzeżenia potencjalnych użytkowników, więc dodanie nowej metody o nazwie wnext!
zasadzie zrobiłoby to, o co proszono, tj.$ ruby -e 'numb=1; numb.next!; puts numb'
zwraca 2 (ponieważ liczba numb została zwiększona)
Oczywiście
next!
metoda musiałaby sprawdzić, czy obiekt był zmienną całkowitą, a nie liczbą rzeczywistą, ale ta powinna być dostępna.źródło
Integer#next
już istnieje (mniej więcej), z wyjątkiem tego, że jest nazywanyInteger#succ
(od „następcy”). AleInteger#next!
(lubInteger#succ!
) byłoby nonsensem: pamiętaj, że metody działają na obiektach , a nie na zmiennych , więcnumb.next!
byłyby dokładnie równe1.next!
, co oznacza, że mutacja 1 byłaby równa 2 .++
byłoby nieznacznie lepsze, ponieważ mogłoby to być cukier składniowy dla przydziału, ale osobiście wolę obecną składnię, w której wszystkie przypisania są wykonywane=
.Integer#pred
pobrać poprzednika.Sprawdź te operatory z rodziny C w irb Rubiego i przetestuj je samodzielnie:
x = 2 # x is 2 x += 2 # x is 4 x++ # x is now 8 ++x # x reverse to 4
źródło
(x++)
nieprawidłowe stwierdzenie w Rubim.