Niedawno nauczyłem się języka programowania Ruby i w sumie jest to dobry język. Ale byłem dość zaskoczony, widząc, że nie było to tak proste, jak się spodziewałem. Mówiąc dokładniej, „zasada najmniejszego zaskoczenia” nie wydawała mi się zbyt szanowana (oczywiście jest to dość subiektywne). Na przykład:
x = true and false
puts x # displays true!
i słynne:
puts "zero is true!" if 0 # zero is true!
Jakie są inne „Gotchas”, o których ostrzegłbyś nowicjusza Rubiego?
true and false
wraca prawda?Odpowiedzi:
Wikipedia Ruby gotchas
Z artykułu:
$
i@
nie wskazują zmiennego typu danych, jak w Perlu, ale raczej działają jako operatory rozpoznawania zasięgu.99.0
podać cyfrę zerową ( ) lub jawną konwersję (99.to_f
). Nie wystarczy dołączyć kropki (99.
) , ponieważ liczby zależą od składni metody.0
,""
i[]
są oceniane w celutrue
. W języku C wyrażenie przyjmuje0 ? 1 : 0
wartość0
(tj. Fałsz). Jednak w Rubim daje wynik1
, ponieważ wszystkie liczby obliczajątrue
; tylkonil
ifalse
oceniaj dofalse
. Następstwem tej reguły jest to, że metody Rubiego zgodnie z konwencją - na przykład wyszukiwanie przy użyciu wyrażeń regularnych - zwracają liczby, ciągi znaków, listy lub inne wartości inne niż fałszywe w przypadku sukcesu, alenil
w przypadku niepowodzenia (np. Niezgodności). Ta konwencja jest również używana w Smalltalk, gdzie tylko specjalne obiektytrue
ifalse
mogą być używane w wyrażeniach logicznych.char
dla znaków). Może to powodować niespodzianki podczas krojenia łańcuchów:"abc"[0]
yields97
(liczba całkowita, reprezentująca kod ASCII pierwszego znaku w ciągu); aby uzyskać"a"
użycie"abc"[0,1]
(podciąg o długości 1) lub"abc"[0].chr
.Notacja
statement until expression
, w przeciwieństwie do równoważnych instrukcji innych języków (np.do { statement } while (not(expression));
W C / C ++ / ...), w rzeczywistości nigdy nie uruchamia instrukcji, jeśli wyrażenie już jesttrue
. Dzieje się tak, ponieważ wstatement until expression
rzeczywistości cukier syntaktyczny się skończył, którego odpowiednik w C / C ++ jest
while (not(expression)) statement;
taki sam, jakstatement if expression
jest odpowiednikiemJednak notacja
w Rubim faktycznie uruchomi instrukcję raz, nawet jeśli wyrażenie jest już prawdziwe.
Greeting << " world!" if Greeting == "Hello"
nie generuje błędu ani ostrzeżenia. Jest to podobne dofinal
zmiennych w Javie, ale Ruby ma również funkcję „zamrażania” obiektu, w przeciwieństwie do Javy.Niektóre funkcje, które różnią się znacznie od innych języków:
Zwykłe operatory dla wyrażeń warunkowych
and
ior
nie są zgodne z normalnymi regułami pierwszeństwa:and
nie wiąże się mocniej niżor
. Ruby ma również operatorów ekspresji||
i&&
które działają zgodnie z oczekiwaniami.def
insidedef
nie robi tego, czego mógłby oczekiwać programista Pythona:Daje to błąd związany z
x
brakiem definicji. Musisz użyćProc
.Funkcje językowe
()
języka Ruby rzuca ostrzeżenie, które zachęca autora do tego, aby nie pomijał , aby uniknąć niejednoznacznego znaczenia kodu. Nieużywanie()
jest nadal powszechną praktyką i może być szczególnie przyjemne użycie Rubiego jako czytelnego dla człowieka języka programowania specyficznego dla domeny, wraz z metodą o nazwiemethod_missing()
.źródło
Nowicjusze będą mieli problem z metodami równości :
Te przykłady powinny wyjaśnić pierwsze 3 metody:
Zauważ, że == , eql? i równy?powinien być zawsze symetryczny: jeśli a == b to b == a.
Zauważ też, że == i eql?są zaimplementowane w klasie Object jako aliasy równe? , więc jeśli utworzysz nową klasę i chcesz == i eql? aby oznaczać coś innego niż zwykła tożsamość, musisz zastąpić je obie. Na przykład:
=== zachowuje się inaczej. Przede wszystkim jest to nie symetryczny (a === b jest nie oznacza, że B === A). Jak powiedziałem, a === b można odczytać jako „a pasuje do b”. Oto kilka przykładów:
Instrukcja case oparta jest na metodzie === :
jest równoważne z tym:
Jeśli zdefiniujesz nową klasę, której instancje reprezentują jakiś rodzaj kontenera lub zakresu (jeśli ma ona coś w rodzaju metody include? Lub match? ), Może okazać się przydatne zastąpienie metody === w następujący sposób:
źródło
Łata małpy . Ruby ma otwarte klasy, więc ich zachowanie można dynamicznie zmieniać w czasie wykonywania ...
Obiekty mogą odpowiadać na niezdefiniowane metody, jeśli
method_missing
lubsend
zostały nadpisane. Wykorzystuje to wywołanie metody opartej na wiadomościach Rubiego. Szyn ' ActiveRecord system wykorzystuje to z dobrym skutkiem.źródło
Poniższy kod mnie zaskoczył. Myślę, że to niebezpieczne rozwiązanie: zarówno łatwe do znalezienia, jak i trudne do debugowania.
To drukuje:
Ale jeśli dodam
comment =
cokolwiek przed blokiem ...Wtedy otrzymuję:
Zasadniczo, gdy zmienna jest zdefiniowana tylko wewnątrz bloku, jest niszczona na końcu bloku, a następnie jest resetowana
nil
po każdej iteracji. Zwykle tego oczekujesz. Ale jeśli zmienną jest zdefiniowana przed blokiem, wówczas zmienna zewnętrzna jest używana wewnątrz bloku i dlatego jej wartość jest trwała między iteracjami.Jednym z rozwiązań byłoby napisanie tego zamiast tego:
Myślę, że wiele osób (w tym ja) ma tendencję do pisania „
a = b if c
” zamiast „a = (c ? b : nil)
”, ponieważ jest to bardziej czytelne, ale oczywiście ma efekty uboczne.źródło
a = (b if c)
aby uzyskać pożądany efekt, bez trójskładnika. Dzieje się tak, ponieważ szacujeb if c
do zera, jeślic
jest fałszywy.Podczas wywoływania
super
bez argumentów, zastąpiona metoda jest w rzeczywistości wywoływana z tymi samymi argumentami, co metoda przesłaniająca.Aby faktycznie zadzwonić
super
bez argumentów, musisz powiedziećsuper()
.źródło
B#hello
maname = 42
przedsuper
, to mówi „cześć 42”.Bloki i metody domyślnie zwracają wartość ostatniego wiersza. Dodawanie
puts
instrukcji na końcu w celu debugowania może powodować nieprzyjemne efekty uboczneźródło
Dziedziczenie nie odgrywa żadnej roli w określaniu widoczności metod w Rubim.
źródło
Miałem wiele problemów ze zrozumieniem zmiennych klas, atrybutów klas i metod klas. Ten kod może pomóc nowicjuszowi:
źródło
jedna rzecz, której się nauczyłem, to ostrożne używanie operatora || =. i zachowaj szczególną ostrożność, jeśli masz do czynienia z wartościami logicznymi. Zwykle używałem a || = b jako catch all, aby nadać „a” domyślną wartość, jeśli wszystko inne zawiodło, a „a” pozostało zerowe. ale jeśli a jest fałszem i b jest prawdą, a zostanie przypisana prawda.
źródło
a = b if a.nil?
lub@a = b unless defined?(@a)
.Bloki są naprawdę ważne do zrozumienia, są używane wszędzie.
Nie potrzebujesz nawiasów wokół parametrów metody. To, czy ich użyjesz, czy nie, zależy od Ciebie. Niektórzy mówią, że zawsze powinieneś ich używać .
Użyj podnoszenia i ratowania do obsługi wyjątków, a nie rzucania i łapania.
Możesz użyć,
;
ale nie musisz, chyba że chcesz umieścić wiele rzeczy w jednej linii.źródło
Miałem problem z mixinami, które zawierają metody instancji i metody klas. Ten kod może pomóc nowicjuszowi:
Na początku pomyślałem, że mogę mieć moduły zarówno z metodami instancji, jak i metodami klas, po prostu robiąc to:
Niestety, metoda number_of_displays nigdy nie zostanie dołączona ani rozszerzona, ponieważ jest to „metoda klasy modułu”. Tylko „metody instancji modułu” mogą być włączone do klasy (jako metody instancji) lub rozszerzone do klasy (jako metody klas). Dlatego musisz umieścić metody instancji swojego mixina w module, a metody klas twojego mixina w innym module (zazwyczaj umieszczasz metody klas w podmodułu "ClassMethods"). Dzięki dołączonej metodzie magicznej możesz w prosty sposób uwzględnić zarówno metody instancji, jak i metody klas w jednym prostym wywołaniu „include Displayable” (jak pokazano w powyższym przykładzie).
Ta mieszanka będzie liczyć każdy ekran na podstawie klasy . Licznik jest atrybutem klasy, więc każda klasa będzie miała swoją własną (Twój program prawdopodobnie zakończy się niepowodzeniem, jeśli uzyskasz nową klasę z klasy Person, ponieważ licznik @number_of_displays dla klasy pochodnej nigdy nie zostanie zainicjowany). Możesz zamienić @number_of_displays na @@ number_of_displays, aby stał się licznikiem globalnym. W takim przypadku każda hierarchia klas będzie miała swój własny licznik. Jeśli chcesz mieć globalny i unikalny licznik, prawdopodobnie powinieneś uczynić go atrybutem modułu.
Wszystko to zdecydowanie nie było dla mnie intuicyjne, kiedy zaczynałem z Rubim.
Nadal nie mogę dowiedzieć się, jak wyczyścić niektóre z tych metod mieszania jako prywatne lub chronione (tylko metoda display i number_of_displays powinny być uwzględnione jako metody publiczne).
źródło
Zwróć uwagę na notację Range.
(Przynajmniej zwrócić większą uwagę niż ja początkowo nie!)
Istnieje różnica między
0..10
(dwiema kropkami) a0...10
(trzema kropkami).Bardzo lubię Rubiego. Ale to kropka kontra kropka-kropka wkurza mnie. Myślę, że taka subtelna podwójna "funkcja", czyli:
nie powinien być w stanie powodować niszczących błędów w moich programach.
źródło
for (i=0; i<max; i++)
andfor (i=0; i<=max; i++)
Myślę, że „
and
” i „or
” są ukłonami w stronę Perla, który jest jednym z bardziej oczywistych „rodziców” Ruby (najważniejszym z pozostałych jest Smalltalk). Oba mają znacznie niższy priorytet (w rzeczywistości niższy niż przypisanie, skąd pochodzi zanotowane zachowanie) niż&&
i||
których operatorów powinieneś używać.Inne rzeczy, o których należy pamiętać, nie są od razu oczywiste:
Tak naprawdę nie wywołujesz metod / funkcji, chociaż tak to wygląda. Zamiast tego, tak jak w Smalltalk, wysyłasz wiadomość do obiektu. Więc
method_missing
jest bardziej jakmessage_not_understood
.jest równa
Symbole są bardzo szeroko stosowane. To te rzeczy, które zaczynają się
:
i nie są od razu oczywiste (cóż, nie były dla mnie), ale im wcześniej się z nimi uporasz, tym lepiej.Ruby jest wielkim zwolennikiem "kaczego pisania", kierując się zasadą, że "jeśli chodzi jak kaczka i kwacze jak kaczka ...", która pozwala na nieformalne zastępowanie obiektów wspólnym podzbiorem metod bez wyraźnego dziedziczenia lub powiązania.
źródło
Jeśli zadeklarujesz setera (inaczej mutator) za pomocą
attr_writer
lubattr_accessor
(lubdef foo=
), uważaj na wywołanie go z wnętrza klasy. Ponieważ zmienne są deklarowane niejawnie, interpreter zawsze musi rozstrzygać,foo = bar
że deklaruje nową zmienną o nazwie foo, zamiast wywoływać metodęself.foo=(bar)
.Dotyczy to również obiektów ActiveRecord Railsów, które otrzymują akcesory zdefiniowane na podstawie pól w bazie danych. Ponieważ nie są one nawet zmiennymi instancji w stylu @, właściwym sposobem ustawiania tych wartości indywidualnie jest użycie
self.value = 123
lubself['value'] = 123
.źródło
Zrozumienie różnicy między klasą Time i Date. Oba są różne i spowodowały problemy podczas używania ich w szynach. Klasa Time czasami koliduje z innymi bibliotekami klas Time obecnymi w standardowej bibliotece ruby / rails. Osobiście dużo czasu zajęło mi zrozumienie, co dokładnie dzieje się w mojej aplikacji rails. Później domyśliłem się, kiedy to zrobiłem
Time.new
Odnosiło się do jakiejś biblioteki w miejscu, którego nawet nie byłem świadomy.
Przepraszam, jeśli nie rozumiem dokładnie tego, co chcę powiedzieć. Jeśli inni napotkali podobne problemy, wyjaśnij ponownie.
źródło
W przeszłości złapałem mnie na tym, że
\n
sekwencja ucieczki znak ( ) nowej linii - między innymi - nie jest obsługiwana przez ciągi znaków w apostrofach. Sam odwrotny ukośnik zostaje pominięty. Musisz użyć podwójnych cudzysłowów, aby ucieczka działała zgodnie z oczekiwaniami.źródło
0 i „” są prawdziwe, jak wskazałeś.
Możesz mieć metodę i moduł / klasę o tej samej nazwie (co ma sens, ponieważ metoda jest faktycznie dodawana do Object i dlatego ma swoją własną przestrzeń nazw).
Nie ma dziedziczenia wielokrotnego, ale często używa się „modułów mieszanych”, aby dodać wspólne metody do wielu klas.
źródło
false
inil
są fałszywe. Wszystkie inne są prawdziwymi wartościami.Metody można zdefiniować na nowo i mogą stać się „drapieżnikiem”, dopóki nie odkryjesz przyczyny. ( Trzeba przyznać, że ten błąd jest prawdopodobnie nieco „trudniejszy” do wykrycia, gdy akcja kontrolera Ruby on Rails zostanie ponownie zdefiniowana przez pomyłkę! )
Biegać:
Ale zadzwoń do tego z włączonymi ostrzeżeniami i możesz zobaczyć powód:
źródło
Myślę, że zawsze dobrze jest używać .length do rzeczy ... ponieważ rozmiar jest obsługiwany przez prawie wszystko, a Ruby ma typy dynamiczne, możesz uzyskać naprawdę dziwne wyniki wywołując .size, gdy masz zły typ ... Wolałbym raczej dostać a NoMethodError: undefined method `length ', więc generalnie nigdy nie wywołuję size dla obiektów w Rubim.
ugryzł mnie więcej niż raz.
Pamiętaj również, że obiekty mają identyfikatory, więc staram się nie używać zmiennych call id lub object_id, aby uniknąć nieporozumień. Jeśli potrzebuję identyfikatora w obiekcie Users, najlepiej nazwać go czymś w rodzaju user_id.
Tylko moje dwa centy
źródło
Jestem nowy w Ruby i podczas mojej pierwszej rundy napotkałem problem dotyczący zmiany liczb zmiennoprzecinkowych / ciągów znaków na liczbę całkowitą. Zacząłem od pływaków i zakodowałem wszystko jako f.to_int . Ale kiedy kontynuowałem i użyłem tej samej metody dla łańcuchów, zostałem rzucony krzywą, gdy przyszło do uruchomienia programu.
Najwyraźniej string nie ma metody to_int , ale zmiennoprzecinkowe i ints tak.
Arbitralne nawiasy też mnie odrzuciły. Widziałem kod z i bez. Zajęło mi trochę czasu, zanim zdałem sobie sprawę, że oba style są akceptowane.
źródło
W odniesieniu do odpowiedzi monkuta,
to_foo
metody Rubiego wskazują na to, jak rygorystyczna będzie konwersja.Krótkie, takie jak
to_i
,to_s
powiedz, że jest leniwe i przekonwertuj je na typ docelowy, nawet jeśli nie można ich dokładnie przedstawić w tym formacie. Na przykład:Dłuższe funkcje jawne, takie jak
to_int
,to_s
oznaczają, że obiekt może być natywnie reprezentowany jako ten typ danych. Na przykładRational
klasa reprezentuje wszystkie liczby wymierne, więc można ją bezpośrednio przedstawić jako liczbę całkowitą Fixnum (lub Bignum) przez wywołanieto_int
.Jeśli nie możesz wywołać dłuższej metody, oznacza to, że obiekt nie może być natywnie reprezentowany w tym typie.
Zasadniczo, jeśli podczas konwersji jesteś leniwy z nazwami metod, Ruby będzie leniwy z konwersją.
źródło
Dlaczego z In Ruby nie
foo = true unless defined?(foo)
zrobisz tego zadania?Ponieważ
foo
jest zdefiniowane jakonil
kiedydefined?(foo)
jest wywołane.źródło
Iteracja po skrótach ruby nie jest gwarantowana w określonej kolejności. (To nie jest błąd, to jest funkcja)
Hash#sort
jest przydatne, jeśli potrzebujesz konkretnego zamówienia.Powiązane pytanie: Dlaczego tablica 1000 par kluczy i wartości w języku Ruby jest zawsze w określonej kolejności?
źródło
Ten raz mnie wkurzył:
źródło
1/2
wartościuje do0
, która nie jest równa0.5
, niezależnie od typu. JednakRational(1, 2) == 0.5
i1.0 == 1
.nie działa. Musisz umieścić zakres w nawiasach, na przykład
więc nie myśli, że dzwonisz
5.each
. Myślę, że jest to kwestia nadrzędna, podobnie jakx = true and false
gotcha.źródło