Jakie są Ruby Gotchas, o których nowicjusz powinien być ostrzeżony? [Zamknięte]

108

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?

MiniQuark
źródło
@ phrases.insert (0, p) OK @ phrases.insert (p) NIC się nie dzieje @phrases << p # OK
Anno2001
dlaczego true and falsewraca prawda?
Jürgen Paul
3
Ponieważ „x = prawda i fałsz” jest w rzeczywistości interpretowane jako „(x = prawda) i fałsz”. Jest to kwestia pierwszeństwa operatorów: „and” ma niższy priorytet niż „=”. Większość innych języków ma odwrotny priorytet, nie wiem, dlaczego wybrali tę kolejność w Railsach, uważam to za bardzo zagmatwane. Jeśli chcesz "normalnego" zachowania, po prostu wpisz "x = (prawda i fałsz)", a x będzie fałszywe.
MiniQuark
4
Innym rozwiązaniem jest użycie „&&” i „||” zamiast „i” i „lub”: zachowują się zgodnie z oczekiwaniami. Na przykład: „x = prawda i& fałsz” powoduje, że x jest fałszem.
MiniQuark
„Zasada najmniejszego zaskoczenia zasada najmniejszego oznacza mojego zaskoczenia.” z en.wikipedia.org/wiki/Ruby_(programming_language)#Philosophy To samo dotyczy Pythona. Miałem podobny cytat o twórcy Pythona, ale zapomniałem, gdzie to było.
Darek Nędza

Odpowiedzi:

59

Wikipedia Ruby gotchas

Z artykułu:

  • Nazwy zaczynające się wielką literą są traktowane jako stałe, dlatego zmienne lokalne powinny zaczynać się małą literą.
  • Znaki $i @nie wskazują zmiennego typu danych, jak w Perlu, ale raczej działają jako operatory rozpoznawania zasięgu.
  • Aby oznaczyć liczby zmiennoprzecinkowe, należy 99.0podać cyfrę zerową ( ) lub jawną konwersję ( 99.to_f). Nie wystarczy dołączyć kropki (99. ) , ponieważ liczby zależą od składni metody.
  • Boolean ocena danych non-logicznych jest surowy: 0, ""i []są oceniane w celu true. W języku C wyrażenie przyjmuje 0 ? 1 : 0wartość 0(tj. Fałsz). Jednak w Rubim daje wynik 1, ponieważ wszystkie liczby obliczają true; tylko nili falseoceniaj do false. 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, ale nilw przypadku niepowodzenia (np. Niezgodności). Ta konwencja jest również używana w Smalltalk, gdzie tylko specjalne obiekty trueifalse mogą być używane w wyrażeniach logicznych.
  • Wersje starsze niż 1.9 nie mają typu danych znakowych (w porównaniu z C, który zapewnia typ chardla znaków). Może to powodować niespodzianki podczas krojenia łańcuchów: "abc"[0]yields 97(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ż jest true. Dzieje się tak, ponieważ w statement until expressionrzeczywistości cukier syntaktyczny się skończył

    until expression
      statement
    end

    , którego odpowiednik w C / C ++ jest while (not(expression)) statement;taki sam, jak statement if expressionjest odpowiednikiem

    if expression
      statement
    end

    Jednak notacja

    begin
      statement
    end until expression

    w Rubim faktycznie uruchomi instrukcję raz, nawet jeśli wyrażenie jest już prawdziwe.

  • Ponieważ stałe są odniesieniami do obiektów, zmiana tego, do czego odnosi się stała, generuje ostrzeżenie, ale modyfikacja samego obiektu nie. Na przykład Greeting << " world!" if Greeting == "Hello"nie generuje błędu ani ostrzeżenia. Jest to podobne do finalzmiennych 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 andi ornie są zgodne z normalnymi regułami pierwszeństwa: andnie wiąże się mocniej niż or. Ruby ma również operatorów ekspresji ||i &&które działają zgodnie z oczekiwaniami.

  • definside defnie robi tego, czego mógłby oczekiwać programista Pythona:

    def a_method
        x = 7
        def print_x; puts x end
        print_x
    end

    Daje to błąd związany z xbrakiem definicji. Musisz użyć Proc.

Funkcje językowe

  • Pominięcie nawiasów wokół argumentów metod może prowadzić do nieoczekiwanych wyników, jeśli metody przyjmują wiele parametrów. Twórcy Rubiego stwierdzili, że pomijanie nawiasów w metodach wieloparametrowych może być niedozwolone w przyszłych wersjach Rubiego; Obecny (listopad 2007) interpreter ()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 nazwie method_missing().
Andy
źródło
1
Ruby 1.9 nie ma również typu danych znakowych. W 1.8, operator indeksu zwrócił Fixnum; w wersji 1.9 jest to równoważne wycinaniu jednoznakowego ciągu.
whitequark
38

Nowicjusze będą mieli problem z metodami równości :

  • a == b : sprawdza, czy a i b są równe. To jest najbardziej przydatne.
  • a.eql? b : sprawdza również, czy a i b są równe, ale czasami jest bardziej rygorystyczne (może na przykład sprawdzać, czy a i b mają ten sam typ). Jest używany głównie w hashes.
  • a. równy? b : sprawdza, czy a i b są tym samym obiektem (kontrola tożsamości).
  • a === b : używane w instrukcjach case (czytam to jako „ a pasuje do b ”).

Te przykłady powinny wyjaśnić pierwsze 3 metody:

a = b = "joe"

a==b       # true
a.eql? b   # true
a.equal? b # true (a.object_id == b.object_id)

a = "joe"
b = "joe"

a==b       # true
a.eql? b   # true
a.equal? b # false (a.object_id != b.object_id)

a = 1
b = 1.0

a==b       # true
a.eql? b   # false (a.class != b.class)
a.equal? b # false

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:

class Person
    attr_reader name
    def == (rhs)
      rhs.name == self.name  # compare person by their name
    end
    def eql? (rhs)
      self == rhs
    end
    # never override the equal? method!
end

=== 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:

# === is usually simply an alias for ==
"joe" === "joe"  # true
"joe" === "bob"  # false

# but ranges match any value they include
(1..10) === 5        # true
(1..10) === 19       # false
(1..10) === (1..10)  # false (the range does not include itself)

# arrays just match equal arrays, but they do not match included values!
[1,2,3] === [1,2,3] # true
[1,2,3] === 2       # false

# classes match their instances and instances of derived classes
String === "joe"   # true
String === 1.5     # false (1.5 is not a String)
String === String  # false (the String class is not itself a String)

Instrukcja case oparta jest na metodzie === :

case a
  when "joe": puts "1"
  when 1.0  : puts "2"
  when (1..10), (15..20): puts "3"
  else puts "4"
end

jest równoważne z tym:

if "joe" === a
  puts "1"
elsif 1.0 === a
  puts "2"
elsif (1..10) === a || (15..20) === a
  puts "3"
else
  puts "4"
end

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:

class Subnet
  [...]
  def include? (ip_address_or_subnet)
    [...]
  end
  def === (rhs)
    self.include? rhs
  end
end

case destination_ip
  when white_listed_subnet: puts "the ip belongs to the white-listed subnet"
  when black_listed_subnet: puts "the ip belongs to the black-listed subnet"
  [...]
end
MiniQuark
źródło
1
Ponadto: a = 'строка'; b = 'строка'; pa == b; a = a.force_encoding 'ASCII-8BIT'; b = b.force_encoding 'UTF-8'; pa == b; pa === b; p a.eql? b; p a.equal? b
Nakilon
18

Poniższy kod mnie zaskoczył. Myślę, że to niebezpieczne rozwiązanie: zarówno łatwe do znalezienia, jak i trudne do debugowania.

(1..5).each do |number|
  comment = " is even" if number%2==0
  puts number.to_s + comment.to_s
end

To drukuje:

1
2 is even
3
4 is even
5

Ale jeśli dodam comment =cokolwiek przed blokiem ...

comment = nil
(1..5).each do |number|
  comment = " is even" if number%2==0
  puts number.to_s + comment.to_s
end

Wtedy otrzymuję:

1
2 is even
3 is even
4 is even
5 is even

Zasadniczo, gdy zmienna jest zdefiniowana tylko wewnątrz bloku, jest niszczona na końcu bloku, a następnie jest resetowana nilpo 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:

comment = number%2==0 ? " is even" : nil

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.

MiniQuark
źródło
4
Równie dobrze możesz przesłonić zmienną o zasięgu zewnętrznym przez (1..5) do | numer; komentarz | ..... Przeczytaj tutaj stackoverflow.com/questions/1654637/ ...
Özgür
6
Wydaje mi się to logiczne. Ten zakres jest typowy dla innych języków, różni się tylko składnią.
g.
Możesz jednak napisać, a = (b if c)aby uzyskać pożądany efekt, bez trójskładnika. Dzieje się tak, ponieważ szacuje b if cdo zera, jeśli cjest fałszywy.
Cameron Martin
16

Podczas wywoływania superbez argumentów, zastąpiona metoda jest w rzeczywistości wywoływana z tymi samymi argumentami, co metoda przesłaniająca.

class A
  def hello(name="Dan")
    puts "hello #{name}"
  end
end

class B < A
  def hello(name)
    super
  end
end

B.new.hello("Bob") #=> "hello Bob"

Aby faktycznie zadzwonić superbez argumentów, musisz powiedzieć super().

Daniel Lucraft
źródło
3
Jeśli B#helloma name = 42przed super, to mówi „cześć 42”.
Andrew Grimm,
14

Bloki i metody domyślnie zwracają wartość ostatniego wiersza. Dodawanie putsinstrukcji na końcu w celu debugowania może powodować nieprzyjemne efekty uboczne

Andrew Grimm
źródło
11

Miałem wiele problemów ze zrozumieniem zmiennych klas, atrybutów klas i metod klas. Ten kod może pomóc nowicjuszowi:

class A
  @@classvar = "A1"
  @classattr = "A2"
  def self.showvars
    puts "@@classvar => "+@@classvar
    puts "@classattr => "+@classattr
  end
end

A.showvars
  # displays:
  # @@classvar => A1
  # @classattr => A2

class B < A
  @@classvar = "B1"
  @classattr = "B2"
end

B.showvars
  # displays:
  # @@classvar => B1
  # @classattr => B2

A.showvars
  # displays:
  # @@classvar => B1   #Class variables are shared in a class hierarchy!
  # @classattr => A2   #Class attributes are not
MiniQuark
źródło
1
Tak, zmienne klasowe mogą być trudne. Myślę, że większość doświadczonych rubistów powiedziałaby, że mądrze jest ich unikać, ponieważ zazwyczaj istnieją inne sposoby rozwiązania problemu bez nich. Niektórzy entuzjaści języków powiedzieliby nawet, że zmienne klas Rubiego są źle zaprojektowane na poziomie języka.
David J.,
8

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.

karina
źródło
Możesz użyć a = b if a.nil?lub @a = b unless defined?(@a).
Andrew Grimm
8
  • 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.

dylanfm
źródło
Jeśli nie planujesz wyjść poza Ruby 1.8.6, ignoruj ​​pareny tak bardzo, jak chcesz. W przeciwnym razie prawdopodobnie lepiej ich użyjesz.
Mike Woodhouse,
7

Miałem problem z mixinami, które zawierają metody instancji i metody klas. Ten kod może pomóc nowicjuszowi:

module Displayable
  # instance methods here
  def display
    puts name
    self.class.increment_displays
  end
  def self.included(base)
    # This module method will be called automatically
    # after this module is included in a class.
    # We want to add the class methods to the class.
    base.extend Displayable::ClassMethods
  end
  module ClassMethods
    # class methods here
    def number_of_displays
      @number_of_displays # this is a class attribute
    end
    def increment_displays
      @number_of_displays += 1
    end
    def init_displays
      @number_of_displays = 0
    end
    # this module method will be called automatically
    # after this module is extended by a class.
    # We want to perform some initialization on a
    # class attribute.
    def self.extended(base)
      base.init_displays
    end
  end
end

class Person
  include Displayable
  def name; @name; end
  def initialize(name); @name=name; end
end

puts Person.number_of_displays # => 0
john = Person.new "John"
john.display # => John
puts Person.number_of_displays # => 1
jack = Person.new "Jack"
jack.display # => Jack
puts Person.number_of_displays # => 2

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:

module Displayable
  def display
    puts name
    self.class.increment_displays
  end
  def self.number_of_displays  # WRONG!
    @number_of_displays
  end
  [...]
end

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

MiniQuark
źródło
7

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) a 0...10(trzema kropkami).

Bardzo lubię Rubiego. Ale to kropka kontra kropka-kropka wkurza mnie. Myślę, że taka subtelna podwójna "funkcja", czyli:

  • łatwe do pomyłki i
  • łatwo przeoczyć oczy, patrząc na kod

nie powinien być w stanie powodować niszczących błędów w moich programach.

que que
źródło
1
Niewiele różni się od for (i=0; i<max; i++)andfor (i=0; i<=max; i++)
g.
Próbowałem dowiedzieć się, jaka jest różnica między 0..10 a 0 ... 10.
Luis D Urraca,
6

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_missingjest bardziej jak message_not_understood.

some_object.do_something(args)

jest równa

some_object.send(:do_something, args) # note the :

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.

Mike Woodhouse
źródło
Dzięki. Jest jedna rzecz, której nienawidzę w metodzie send : umożliwia wywoływanie metod prywatnych nawet poza klasą! Auć.
MiniQuark
1
@MiniQuark: to właśnie uwielbiam w metodzie wysyłania!
Andrew Grimm,
6

Jeśli zadeklarujesz setera (inaczej mutator) za pomocą attr_writerlubattr_accessor (lub def 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).

class Thing
  attr_accessor :foo
  def initialize
    @foo = 1      # this sets @foo to 1
    self.foo = 2  # this sets @foo to 2
    foo = 3       # this does *not* set @foo
  end
end

puts Thing.new.foo #=> 2

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 = 123lubself['value'] = 123 .

AlexChaffee
źródło
5

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.

Chirantan
źródło
4

W przeszłości złapałem mnie na tym, że \nsekwencja 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.

John Topley
źródło
1
A to różni się od innego języka?
Robert Gamble,
Na przykład Java. Pojedyncze cudzysłowy w Javie mogą być używane tylko do umieszczania pojedynczego znaku, a nie do ciągów.
John Topley,
1
Jest to zgodne z każdym językiem, który pozwala używać pojedynczych cudzysłowów dla łańcuchów i dlatego tak jest.
singpolyma
@John: prawda, ale „\ n” w Javie nadal będzie znakiem nowej linii.
Jorn
1
Ale w Javie pojedyncze cudzysłowy tworzą tylko wartości typu char. Nie sznurki. To jest różnica.
jmucchiello,
4
x = (true and false) # x is false

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.

singpolyma
źródło
0 == prawda // argh kompilator c w moim mózgu eksploduje !!
kenny
1
0 == true daje fałsz w Rubim. To, że 0 jest prawdziwe, ma sens, ponieważ prawda jest obiektem w Rubim. W C 0 po prostu ma taką samą reprezentację jak fałsz.
Jules,
Tylko w warunku w Rubim falsei nilsą fałszywe. Wszystkie inne są prawdziwymi wartościami.
rubyprince
4

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ę! )

#demo.rb
class Demo

  def hello1
    p "Hello from first definition"
  end

  # ...lots of code here...
  # and you forget that you have already defined hello1

  def hello1
    p "Hello from second definition"
  end

end
Demo.new.hello1

Biegać:

$ ruby demo.rb
=> "Hello from second definition"

Ale zadzwoń do tego z włączonymi ostrzeżeniami i możesz zobaczyć powód:

$ ruby -w demo.rb
demo.rb:10: warning: method redefined; discarding old hello1
=> "Hello from second definition"
Zabba
źródło
Gdybym mógł, dałbym +100 do użycia ostrzeżeń.
Andrew Grimm
3

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

danmayer
źródło
2

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.

irb(main):003:0* str_val = '5.0'
=> "5.0"
irb(main):006:0> str_val.to_int
NoMethodError: undefined method `to_int' for "5.0":String
        from (irb):6
irb(main):005:0* str_val.to_i
=> 5


irb(main):007:0> float_val = 5.0
=> 5.0
irb(main):008:0> float_val.to_int
=> 5
irb(main):009:0> float_val.to_i
=> 5
irb(main):010:0>

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.

monkut
źródło
2

W odniesieniu do odpowiedzi monkuta, to_foometody Rubiego wskazują na to, jak rygorystyczna będzie konwersja.

Krótkie, takie jak to_i, to_spowiedz, ż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:

"10".to_i == 10
:foo.to_s == "foo"

Dłuższe funkcje jawne, takie jak to_int, to_soznaczają, że obiekt może być natywnie reprezentowany jako ten typ danych. Na przykład Rationalklasa reprezentuje wszystkie liczby wymierne, więc można ją bezpośrednio przedstawić jako liczbę całkowitą Fixnum (lub Bignum) przez wywołanie to_int.

Rational(20,4).to_int == 5

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

Łukasz
źródło
1
Czy „leniwy” to właściwe słowo?
Andrew Grimm,
0

Ten raz mnie wkurzył:

1/2 == 0.5 #=> false
1/2 == 0   #=> true
Andrei
źródło
Uważam, że zachowałoby się to dokładnie tak samo w Javie, C i C ++.
Larry
To zabawne, nawet o tym nie myślałem, ale jeśli otworzysz irb i spróbujesz tego, ma to sens: Więc (1/2) to Fixnum, a (0.5) to Float. Wiemy, że Fixnim! = Float.
DemitryT
2
@DemitryT Myślę, że prostszym powodem jest to, że 1/2wartościuje do 0, która nie jest równa 0.5, niezależnie od typu. Jednak Rational(1, 2) == 0.5i 1.0 == 1.
Max Nanasy,
czkawka języka uniwersalnego. jest to coś, co powinien wiedzieć ktoś nowy w Ruby ORAZ programowanie.
dtc
0
1..5.each {|x| puts x}

nie działa. Musisz umieścić zakres w nawiasach, na przykład

(1..5).each {|x| puts x}

więc nie myśli, że dzwonisz 5.each. Myślę, że jest to kwestia nadrzędna, podobnie jak x = true and falsegotcha.

Andrew Grimm
źródło
Zamiast tego nazwałbym to nawiasem. Po drugie, jeśli jakikolwiek kod wygląda tak, jakby miał problem z wartością zwracaną / priorytetem, i tak powinien być otoczony nawiasami. Więc, dla mnie, nie ma nic specjalnego w tym „łapaniu”. Możesz dalej pisać wszystkie kombinacyjne „pułapki”, ale to byłaby strata czasu. Szczerze, kolego, nawet gdybyś miał na to spodziewany skutek, nadal wolałbym otoczenie w nawiasach.
Özgür