Vor welchen Ruby Gotchas sollte ein Neuling gewarnt werden? [geschlossen]


108

Ich habe kürzlich die Programmiersprache Ruby gelernt und alles in allem ist es eine gute Sprache. Aber ich war ziemlich überrascht zu sehen, dass es nicht so einfach war, wie ich erwartet hatte. Genauer gesagt schien mir die "Regel der geringsten Überraschung" nicht sehr respektiert zu sein (das ist natürlich ziemlich subjektiv). Beispielsweise:

x = true and false
puts x  # displays true!

und die berühmten:

puts "zero is true!" if 0  # zero is true!

Was sind die anderen "Gotchas", vor denen Sie einen Ruby-Neuling warnen würden?


@ phrases.insert (0, p) OK @ phrases.insert (p) NICHTS passiert @phrases << p # OK
Anno2001

Warum ist die true and falseRückkehr wahr?
Jürgen Paul

3
Weil "x = wahr und falsch" tatsächlich als "(x = wahr) und falsch" interpretiert wird. Es ist eine Frage der Priorität des Operators: "und" haben eine niedrigere Priorität als "=". Die meisten anderen Sprachen haben die umgekehrte Priorität. Ich weiß nicht, warum sie diese Reihenfolge in Rails gewählt haben. Ich finde sie sehr verwirrend. Wenn Sie das "normale" Verhalten wünschen, geben Sie einfach "x = (wahr und falsch)" ein, dann ist x falsch.
MiniQuark

4
Eine andere Lösung ist die Verwendung von "&&" und "||" anstelle von "und" und "oder": Sie verhalten sich wie erwartet. Zum Beispiel: "x = wahr && falsch" führt dazu, dass x falsch ist.
MiniQuark

„Das Prinzip der geringsten Überraschung Mittel mindestens Prinzip meiner Überraschung.“ von en.wikipedia.org/wiki/Ruby_(programming_language)#Philosophy Gleiches gilt für Python. Ich hatte ein ähnliches Zitat über Pythons Schöpfer, aber ich vergesse, wo es war.
Darek Nędza

Antworten:


59

Wikipedia Ruby Fallstricke

Aus dem Artikel:

  • Namen, die mit einem Großbuchstaben beginnen, werden als Konstanten behandelt, daher sollten lokale Variablen mit einem Kleinbuchstaben beginnen.
  • Die Zeichen $und @geben keinen variablen Datentyp wie in Perl an, sondern fungieren als Bereichsauflösungsoperatoren.
  • Um Gleitkommazahlen zu bezeichnen, muss eine Nullstelle ( 99.0) oder eine explizite Konvertierung ( 99.to_f) folgen . Es reicht nicht aus, einen Punkt ( 99.) anzuhängen , da Zahlen für die Methodensyntax anfällig sind.
  • Boolesche Auswertung von nicht-boolean Daten ist streng: 0, ""und []sind alle ausgewertet true. In C ist die Expression 0 ? 1 : 0ausgewertet 0(dh false). In Ruby ergibt es jedoch 1, wie alle Zahlen auswerten true; nur nilund falsebewerten zu false. Eine Folge dieser Regel ist, dass Ruby-Methoden gemäß Konvention - beispielsweise Suchvorgänge mit regulären Ausdrücken - Zahlen, Zeichenfolgen, Listen oder andere nicht falsche Werte bei Erfolg, aber nilbei Misserfolg (z. B. Nichtübereinstimmung) zurückgeben. Diese Konvention wird auch in Smalltalk verwendet, wo nur die speziellen Objekte trueund falsein einem booleschen Ausdruck verwendet werden können.
  • Versionen vor 1.9 haben keinen Zeichendatentyp (vergleiche mit C, das den Typ charfür Zeichen bereitstellt ). Dies kann beim Schneiden von Zeichenfolgen zu Überraschungen führen: "abc"[0]Erträge 97(eine Ganzzahl, die den ASCII-Code des ersten Zeichens in der Zeichenfolge darstellt); zu erhalten "a"Verwendung "abc"[0,1](eine Teillänge 1) oder "abc"[0].chr.
  • Die Notation statement until expressionführt im Gegensatz zu den entsprechenden Anweisungen anderer Sprachen (z. B. do { statement } while (not(expression));in C / C ++ / ...) die Anweisung tatsächlich nie aus, wenn der Ausdruck bereits vorhanden ist true. Dies liegt daran, dass statement until expressiontatsächlich syntaktischer Zucker vorbei ist

    until expression
      statement
    end

    , dessen Äquivalent in C / C ++ while (not(expression)) statement;genau so statement if expressionist wie

    if expression
      statement
    end

    Allerdings die Notation

    begin
      statement
    end until expression

    In Ruby wird die Anweisung tatsächlich einmal ausgeführt, auch wenn der Ausdruck bereits wahr ist.

  • Da Konstanten Verweise auf Objekte sind, wird beim Ändern der Konstanten eine Warnung generiert, beim Ändern des Objekts selbst jedoch nicht. Greeting << " world!" if Greeting == "Hello"Erzeugt beispielsweise keinen Fehler oder keine Warnung. Dies ähnelt finalVariablen in Java, aber Ruby verfügt im Gegensatz zu Java auch über die Funktionalität, ein Objekt "einzufrieren".

Einige Funktionen, die sich insbesondere von anderen Sprachen unterscheiden:

  • Die üblichen Operatoren für bedingte Ausdrücke andund orfolgen nicht den normalen Vorrangregeln: andBindet nicht enger als or. Ruby hat auch Ausdruck Operatoren ||und &&die Arbeit wie erwartet.

  • definside defmacht nicht das, was ein Python-Programmierer erwarten könnte:

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

    Dies gibt einen Fehler darüber, dass er xnicht definiert wurde. Sie müssen a verwendenProc .

Sprachmerkmale

  • Das Weglassen von Klammern um Methodenargumente kann zu unerwarteten Ergebnissen führen, wenn die Methoden mehrere Parameter annehmen. Die Ruby-Entwickler haben angegeben, dass das Weglassen von Klammern bei Multi-Parameter-Methoden in zukünftigen Ruby-Versionen möglicherweise nicht zulässig ist. Der aktuelle Ruby-Interpreter (November 2007) gibt eine Warnung aus, die den Autor dazu ermutigt, nicht auszulassen (), um eine mehrdeutige Bedeutung des Codes zu vermeiden. Die Nichtverwendung ()ist nach wie vor gängige Praxis und kann besonders hilfreich sein, wenn Ruby zusammen mit der aufgerufenen Methode als lesbare domänenspezifische Programmiersprache verwendet wird method_missing().

1
Ruby 1.9 fehlt auch der Zeichendatentyp. In 1.8 gab der Indexoperator eine Fixnummer zurück. In 1.9 entspricht dies dem Schneiden einer Zeichenfolge mit einem Zeichen.
Whitequark

38

Neulinge werden Probleme mit Gleichstellungsmethoden haben :

  • a == b : prüft, ob a und b gleich sind. Dies ist am nützlichsten.
  • a.eql? b : Überprüft auch, ob a und b gleich sind, ist aber manchmal strenger (es kann beispielsweise überprüft werden, ob a und b denselben Typ haben). Es wird hauptsächlich in Hashes verwendet.
  • a.equal? b : prüft, ob a und b dasselbe Objekt sind (Identitätsprüfung).
  • a === b : wird in case-Anweisungen verwendet (ich habe es als " a entspricht b " gelesen ).

Diese Beispiele sollten die ersten drei Methoden verdeutlichen:

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

Beachten Sie, dass == , Gl.? und gleich? sollte immer symmetrisch sein: wenn a == b dann b == a.

Beachten Sie auch, dass == und eql? Sind beide in der Klasse Object als Aliase gleich implementiert ? Also , wenn Sie eine neue Klasse erstellen und == und eql wollen? Um etwas anderes als eine einfache Identität zu bedeuten, müssen Sie beide überschreiben. Beispielsweise:

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

Die Methode === verhält sich anders. Erstens ist es nicht symmetrisch (a === b bedeutet nicht , dass b === a). Wie gesagt, Sie können a === b als "a entspricht b" lesen. Hier einige Beispiele:

# === 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)

Die case- Anweisung basiert auf der Methode === :

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

ist gleichbedeutend damit:

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

Wenn Sie eine neue Klasse , deren Instanzen repräsentieren eine Art von Behälter oder einen Bereich zu definieren (wenn es so etwas wie eine hat ? Umfassen oder ein ? Match - Methode), dann finden Sie könnte es sinnvoll , die außer Kraft zu setzen === Methode wie folgt:

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

1
Auch: 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

Der folgende Code hat mich überrascht. Ich denke, es ist ein gefährlicher Fall: sowohl leicht zu treffen als auch schwer zu debuggen.

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

Dies druckt:

1
2 is even
3
4 is even
5

Aber wenn ich nur comment =etwas vor dem Block hinzufüge ...

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

Dann bekomme ich:

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

Wenn eine Variable nur innerhalb eines Blocks definiert wird, wird sie am Ende des Blocks zerstört und nilbei jeder Iteration auf zurückgesetzt. Das erwarten Sie normalerweise. Wenn die Variable jedoch vor dem Block definiert ist , wird die äußere Variable innerhalb des Blocks verwendet, und ihr Wert bleibt daher zwischen den Iterationen bestehen.

Eine Lösung wäre, dies stattdessen zu schreiben:

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

Ich denke, viele Leute (einschließlich mir) neigen dazu, " a = b if c" statt " a = (c ? b : nil)" zu schreiben , weil es besser lesbar ist, aber offensichtlich Nebenwirkungen hat.


4
Sie können die Outher-Scope-Variable genauso gut mit (1..5) do | number; comment | beschatten ..... Lesen Sie hier stackoverflow.com/questions/1654637/…
Özgür

6
Das erscheint mir logisch. Dieses Scoping ist typisch für andere Sprachen, nur die Syntax unterscheidet sich.
g.

Sie können jedoch schreiben a = (b if c), um den gewünschten Effekt ohne Ternär zu erzielen. Dies liegt daran b if c, dass null bewertet wird, wenn ces sich um Falsey handelt.
Cameron Martin

16

Beim Aufruf superohne Argumente wird die überschriebene Methode tatsächlich mit denselben Argumenten wie die überschreibende Methode aufgerufen.

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"

Um tatsächlich superohne Argumente anzurufen , müssen Sie sagen super().


3
Wenn B#hellohat name = 42vor dem super, dann heißt es „Hallo 42“.
Andrew Grimm

14

Blöcke und Methoden geben standardmäßig den Wert der letzten Zeile zurück. Das Hinzufügen von putsAnweisungen zum Debuggen am Ende kann unangenehme Nebenwirkungen verursachen



11

Ich hatte große Probleme, Klassenvariablen, Klassenattribute und Klassenmethoden zu verstehen. Dieser Code könnte einem Neuling helfen:

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

1
Ja, Klassenvariablen können schwierig sein. Ich denke, die meisten erfahrenen Rubyisten würden sagen, dass es ratsam ist, sie zu vermeiden, da es normalerweise andere Möglichkeiten gibt, ein Problem ohne sie zu lösen. Einige Sprachbegeisterte würden sogar sagen, dass Rubys Klassenvariablen auf Sprachebene schlecht gestaltet sind.
David J.

8

Eine Sache, die ich gelernt habe, war, den Operator || = vorsichtig zu verwenden. und seien Sie besonders vorsichtig, wenn Sie mit Booleschen Werten zu tun haben. Normalerweise habe ich a || = b als Catch All verwendet, um 'a' einen Standardwert zu geben, wenn alles andere fehlgeschlagen ist und 'a' gleich Null geblieben ist. aber wenn a falsch und b wahr ist, wird a wahr zugewiesen.


Sie können a = b if a.nil?oder verwenden @a = b unless defined?(@a).
Andrew Grimm

8
  • Blöcke sind wirklich wichtig zu verstehen, sie werden überall verwendet.

  • Sie benötigen keine Klammern um Methodenparameter. Ob Sie sie verwenden oder nicht, liegt bei Ihnen. Einige sagen, Sie sollten sie immer verwenden .

  • Verwenden Sie Raise and Rescue für die Ausnahmebehandlung, nicht werfen und fangen.

  • Sie können verwenden ;, müssen es aber nicht, es sei denn, Sie möchten mehrere Dinge in eine Zeile setzen.


Wenn Sie nicht vorhaben, über Ruby 1.8.6 hinauszugehen, ignorieren Sie Parens so oft Sie möchten. Andernfalls ist es wahrscheinlich besser, sie zu verwenden.
Mike Woodhouse

7

Ich hatte Probleme mit Mixins, die Instanzmethoden und Klassenmethoden enthalten . Dieser Code könnte einem Neuling helfen:

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

Zuerst dachte ich, ich könnte Module mit Instanzmethoden und Klassenmethoden haben, indem ich einfach Folgendes mache:

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

Leider wird die Methode number_of_displays niemals aufgenommen oder erweitert, da es sich um eine " Modulklassenmethode " handelt. Nur "Modulinstanzmethoden" können in eine Klasse aufgenommen werden (als Instanzmethoden) oder in eine Klasse erweitert werden (als Klassenmethoden). Aus diesem Grund müssen Sie die Instanzmethoden Ihres Mixins in ein Modul und die Klassenmethoden Ihres Mixins in ein anderes Modul einfügen (normalerweise fügen Sie die Klassenmethoden in ein Submodul "ClassMethods" ein). Dank der enthaltenen magischen Methode können Sie es einfach machen, sowohl Instanzmethoden als auch Klassenmethoden in nur einem einfachen Aufruf "include Displayable" einzuschließen (wie im obigen Beispiel gezeigt).

Dieses Mixin zählt jede Anzeige pro Klasse . Der Zähler ist ein Klassenattribut, daher hat jede Klasse ein eigenes (Ihr Programm schlägt wahrscheinlich fehl, wenn Sie eine neue Klasse aus der Person-Klasse ableiten, da der Zähler @number_of_displays für die abgeleitete Klasse niemals initialisiert wird). Möglicherweise möchten Sie @number_of_displays durch @@ number_of_displays ersetzen , um daraus einen globalen Zähler zu machen. In diesem Fall hat jede Klassenhierarchie einen eigenen Zähler. Wenn Sie einen globalen und eindeutigen Zähler möchten, sollten Sie ihn wahrscheinlich zu einem Modulattribut machen.

All dies war definitiv nicht intuitiv für mich, als ich mit Ruby anfing.

Ich kann immer noch nicht herausfinden, wie einige dieser Mixin-Methoden sauber privat oder geschützt werden können (nur die Methode display und number_of_displays sollten als öffentliche Methoden enthalten sein).


7

Achten Sie auf die Bereichsnotation.

(Zumindest, mehr Aufmerksamkeit schenken , als ich tat zunächst!)

Es gibt einen Unterschied zwischen 0..10 (zwei Punkten) und 0...10(drei Punkten).

Ich mag Ruby sehr. Aber dieses Punkt-Punkt-gegen-Punkt-Punkt-Punkt-Ding nervt mich. Ich denke, dass solch ein subtiles "Feature" mit doppelter Syntax das ist:

  • leicht zu tippen, und
  • leicht mit den Augen zu übersehen, während Sie über den Code blicken

sollte nicht in der Lage sein, verheerende Fehler in meinen Programmen zu verursachen.


1
Nicht viel anders als for (i=0; i<max; i++)undfor (i=0; i<=max; i++)
g.

Ich habe versucht herauszufinden, was der Unterschied zwischen 0..10 und 0 ... 10 ist.
Luis D Urraca

6

Ich denke, " and" und " or" sind Anspielungen auf Perl, einen von Rubys offensichtlicheren "Eltern" (der prominenteste andere ist Smalltalk). Sie haben beide eine viel niedrigere Priorität (niedriger als die Zuordnung, von der das festgestellte Verhalten stammt) als &&und|| welche Operatoren Sie verwenden sollten.

Andere Dinge, die Sie beachten sollten, sind nicht sofort offensichtlich:

Sie rufen Methoden / Funktionen nicht wirklich auf, obwohl es irgendwie so aussieht. Stattdessen senden Sie wie in Smalltalk eine Nachricht an ein Objekt. Ist method_missingalso wirklich eher so message_not_understood.

some_object.do_something(args)

ist äquivalent zu

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

Symbole sind sehr verbreitet. Das sind die Dinge, die anfangen :und die nicht sofort offensichtlich sind (nun, sie waren nicht für mich), aber je früher man sich mit ihnen auseinandersetzt, desto besser.

Ruby ist ein großer Fan von "Ententypisierung" und folgt dem Prinzip "Wenn es wie eine Ente läuft und wie eine Ente quakt ...", das das informelle Ersetzen von Objekten durch eine gemeinsame Teilmenge von Methoden ohne explizite Vererbung oder Mixin-Beziehung ermöglicht.


Vielen Dank. Eines hasse ich an der send- Methode: Sie können damit private Methoden auch außerhalb der Klasse aufrufen! Autsch.
MiniQuark

1
@MiniQuark: Das liebe ich an der Sendemethode!
Andrew Grimm

6

Wenn Sie einen Setter (auch bekannt als Mutator) mit attr_writeroder attr_accessor(oder def foo=) deklarieren , müssen Sie ihn nicht innerhalb der Klasse aufrufen. Da Variablen implizit deklariert werden, muss der Interpreter immer foo = bareine neue Variable mit dem Namen foo deklarieren, anstatt die Methode aufzurufen 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

Dies gilt auch für Rails ActiveRecord-Objekte, bei denen Accessoren basierend auf Feldern in der Datenbank definiert werden. Da es sich nicht einmal um Instanzvariablen im @ -Stil handelt, können Sie diese Werte mit self.value = 123oder einzeln festlegen self['value'] = 123.


5

Den Unterschied zwischen Zeit- und Datumsklasse verstehen. Beide sind unterschiedlich und haben Probleme bei der Verwendung in Schienen verursacht. Die Zeitklasse steht manchmal in Konflikt mit anderen Zeitklassenbibliotheken, die in der Standard-Ruby / Rails-Bibliothek vorhanden sind. Ich persönlich habe viel Zeit gebraucht, um zu verstehen, was genau in meiner Rails-App vor sich ging. Später dachte ich mir, wann ich es tat

Time.new

Es bezog sich auf eine Bibliothek an einem Ort, den ich nicht einmal kannte.

Entschuldigung, wenn mir nicht klar ist, was ich genau sagen möchte. Wenn andere mit ähnlichen Problemen konfrontiert waren, erklären Sie dies bitte erneut.


4

Eines, das mich in der Vergangenheit aufgefallen ist, ist, dass die \nEscape-Sequenz newline character ( ) unter anderem nicht durch Zeichenfolgen in einfachen Anführungszeichen unterstützt wird. Der Backslash selbst wird ausgeblendet. Sie müssen doppelte Anführungszeichen verwenden, damit die Escape-Funktion wie erwartet funktioniert.


1
Und das ist anders als in welcher anderen Sprache?
Robert Gamble

Zum einen Java. Einfache Anführungszeichen in Java können nur verwendet werden, um ein einzelnes Zeichen einzuschließen, keine Zeichenfolgen.
John Topley

1
Dies steht im Einklang mit jeder Sprache, in der Sie einfache Anführungszeichen für Zeichenfolgen verwenden können, und aus diesem Grund.
Singpolym

@ John: stimmt, aber '\ n' in Java wird immer noch das Zeilenumbruchzeichen sein.
Jorn

1
In Java erstellen einfache Anführungszeichen jedoch nur Werte vom Typ char. Keine Saiten. Das ist der Unterschied.
jmucchiello

4
x = (true and false) # x is false

0 und '' sind wahr, wie Sie betont haben.

Sie können eine Methode und ein Modul / eine Klasse mit demselben Namen haben (was sinnvoll ist, da die Methode tatsächlich zu Object hinzugefügt wird und somit einen eigenen Namespace hat).

Es gibt keine Mehrfachvererbung, aber häufig werden "Mixin-Module" verwendet, um mehreren Klassen gemeinsame Methoden hinzuzufügen.


0 == true // argh der c-Compiler in meinem Gehirn explodiert !!
Kenny

1
0 == true gibt in Ruby false an. Dass 0 wahr ist, macht Sinn, weil wahr ein Objekt in Ruby ist. In C 0 hat zufällig die gleiche Darstellung wie false.
Jules

In einem Zustand in Ruby nur falseund nilsind die falschen. Alle anderen sind wahre Werte.
Rubyprince

4

Methoden können neu definiert werden und zu einem Geisteskratzer werden, bis Sie die Ursache entdecken. ( Zugegeben, dieser Fehler ist wahrscheinlich etwas "schwerer" zu erkennen, wenn die Aktion eines Ruby on Rails-Controllers versehentlich neu definiert wird! )

#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

Lauf:

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

Aber rufen Sie es mit aktivierten Warnungen auf und Sie können den Grund sehen:

$ ruby -w demo.rb
demo.rb:10: warning: method redefined; discarding old hello1
=> "Hello from second definition"

Ich würde +100 Warnungen verwenden, wenn ich könnte.
Andrew Grimm

3

Ich denke, es ist immer gut, .length für Dinge zu verwenden ... da die Größe von fast allem unterstützt wird und Ruby dynamische Typen hat, können Sie wirklich seltsame Ergebnisse erzielen, wenn Sie .size aufrufen, wenn Sie den falschen Typ haben ... Ich würde es viel lieber bekommen a NoMethodError: undefinierte Methode `length ', daher rufe ich im Allgemeinen nie die Größe von Objekten in Ruby auf.

hat mich mehr als einmal gebissen.

Denken Sie auch daran, dass Objekte IDs haben. Daher versuche ich, keine Variablen mit dem Namen id oder object_id zu verwenden, um Verwirrung zu vermeiden. Wenn ich eine ID für ein Benutzerobjekt benötige, ist es am besten, sie so etwas wie user_id zu nennen.

Nur meine zwei Cent


2

Ich bin neu in Ruby und habe in meiner ersten Runde ein Problem mit dem Ändern von Floats / Strings in eine Ganzzahl festgestellt. Ich habe mit den Floats angefangen und alles als f.to_int codiert . Aber als ich fortfuhr und die gleiche Methode für Strings verwendete, wurde mir beim Ausführen des Programms eine Kurve geworfen.

Anscheinend hat ein String keine to_int- Methode, Floats und Ints jedoch.

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>

Willkürliche Klammern warfen mich auch zuerst. Ich habe einen Code mit und einen ohne gesehen. Es dauerte eine Weile, bis mir klar wurde, dass beide Stile akzeptiert werden.


2

Im Zusammenhang mit der Antwort von monkut deuten Rubys to_fooMethoden darauf hin, wie streng eine Konvertierung sein wird.

Kurze wie to_i, to_ssagen Sie, dass es faul sein soll, und konvertieren Sie sie in den Zieltyp, auch wenn sie in diesem Format nicht genau dargestellt werden können. Beispielsweise:

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

Die längeren expliziten Funktionen wie to_int, to_sbedeutet , dass das Objekt nativ als diese Art von Daten dargestellt werden. Beispielsweise Rationalrepräsentiert die Klasse alle rationalen Zahlen, sodass sie durch Aufrufen direkt als Fixnum- (oder Bignum-) Ganzzahl dargestellt werden kann to_int.

Rational(20,4).to_int == 5

Wenn Sie die längere Methode nicht aufrufen können, bedeutet dies, dass das Objekt in diesem Typ nicht nativ dargestellt werden kann.

Wenn Sie also beim Konvertieren mit den Methodennamen faul sind, ist Ruby mit der Konvertierung faul.


1
Ist "faul" hier das richtige Wort?
Andrew Grimm


1

Es ist nicht garantiert, dass die Iteration über Rubin-Hashes in einer bestimmten Reihenfolge erfolgt. (Es ist kein Fehler, es ist ein Feature)

Hash#sort ist nützlich, wenn Sie eine bestimmte Bestellung benötigen.

Verwandte Frage: Warum sind Rubys Array von 1000 Hashes-Schlüssel- und Wertepaaren immer in einer bestimmten Reihenfolge?


4
Dies gilt nicht ab 1.9: "In Ruby 1.9 werden Hash-Elemente jedoch in ihrer Einfügereihenfolge iteriert" aus der Ruby-Programmiersprache
Özgür,

0

Dieser hat mich einmal wütend gemacht:

1/2 == 0.5 #=> false
1/2 == 0   #=> true

Ich glaube, dies würde sich in Java, C und C ++ genauso verhalten.
Larry

Das ist lustig, ich habe nicht einmal darüber nachgedacht, aber wenn Sie irb öffnen und dies versuchen, macht es Sinn: Also (1/2) ist ein Fixnum und (0.5) ist ein Float. Und wir wissen, dass Fixnim! = Float.
DemitryT

2
@DemitryT Ich denke, der einfachere Grund ist, dass 1/2ausgewertet wird 0, was nicht gleich ist 0.5, unabhängig vom Typ. Jedoch Rational(1, 2) == 0.5und 1.0 == 1.
Max Nanasy

universeller Sprachschluckauf hier. Dies ist etwas, das jemand neu in Ruby und Programmierung wissen sollte.
dtc

0
1..5.each {|x| puts x}

funktioniert nicht Sie müssen den Bereich in Klammern setzen, wie z

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

Sie glauben also nicht, dass Sie anrufen 5.each. Ich denke, dies ist ein Vorrang, genau wie die x = true and falseGotcha.


Ich würde es stattdessen Klammern nennen. Zweitens sollte ein Code, der ein Problem mit dem Rückgabewert / der Priorität hat, ohnehin von Klammern umgeben sein. Für mich gibt es also nichts Besonderes an diesem "Gotcha". Sie können weiterhin alle kombinatorischen "Fallstricke" schreiben, was jedoch Zeitverschwendung wäre. Ehrlich gesagt, Kumpel, selbst wenn Sie das erwartete Ergebnis hätten, würde ich es immer noch vorziehen, mit Klammern zu umgeben.
Özgür
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.