Die Programmiersprache Ruby

Blog| Forum| Wiki  

Die Artikel RubyWiki:FAQ und Pitfalls überschneiden sich inhaltlich. Bitte hilf mit, das zu verbessern, indem du die Artikel unter einem Namen zusammenführst.
Bitte entferne anschließend den Redundanz-Baustein.

Hier eine Sammlung von beliebten Fettnäpfchen, die Ruby für einen aufgestellt hat.

Inhaltsverzeichnis

Rückgabewert von Bang-Methoden

Methoden, die mit einem Ausrufezeichen enden, verändern das Objekt selbst. Typischerweise geben sie den Wert des Objekts zurück, z.B. bei String#strip!. Wenn strip! jedoch keine Ersetzung durchführt, wird nil zurückgegeben. Folgender Code ist also problematisch:

1
2
3
4
5
s = mystring.strip!  # s k��nnte auch nil enthalten

# Bsp:
s = "x".strip!   # s ist nil
s = " x ".strip! # s ist "x"

Es ist also oft keine gute Idee, Return-Werte von !-Methoden zu verwenden.

SyntaxError am Ende der Datei

Wenn man irgendwo mal ein end vergisst, läuft der Interpreter gerne mal bis zum Ende der Datei durch und gibt erst dort eine Fehlermeldung. Es ist dann mitunter recht schwer, die Zeile im Quellcode zu finden, wo man das end vergessen hat. In solchen Fällen hilft nur, große Codebereiche mit =begin..=end auszukommentieren, um den Fehler sukzessive zu finden.

Was hier hilft, ist ein Editor mit automatischer Einrückung (z. B. Vim oder auch Emacs).

Seit Ruby 1.9 meckert Ruby auch, wenn bei angegebener -w-Option (VERBOSE-Modus) die Einrückungen nicht übereinstimmen.

Verschachtelte Module/Klassen

Werden verschachtelte Module oder Klassen mit der ::-Notation erstellt, verhält sich der Geltungsbereich von Konstanten anders, als man möglicherweise erwarten würde. Ein Beispiel soll dies verdeutlichen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Funktioniert:
module A
  KONSTANTE = :Wert
  module B
    puts KONSTANTE
  end
end

# Funktioniert nicht:
module A
  KONSTANTE = :Wert
end
module A::B
  puts KONSTANTE  # Fehler!
end

Der Grund hierfür ist, dass die Notation "module A::B" nur den Namensbereich (Scope) des Moduls A::B öffnet. Im ersten Beispiel dagegen wird zuerst der Namensbereich des Moduls A, dann jener des Moduls B geöffnet.

Kein ++ Operator (Post und Pre increment/decrement)

Ruby als OOP-Sprache will nicht zu viel Code-Misch-Masch, Stichwort: Eleganz. Matz meinte, daß der ++ Operator wenig Sinn in Ruby macht, d.h. jeder Aufruf einer Methode geschieht in Form einer Message (wie .to_s) Daher funktioniert folgende Syntax:

1
2
3
a_variable  = 1
a_variable += 1
puts a_variable # => 2

Dies funktioniert jedoch nicht:

1
2
3
4
5
a_variable  = 1
a_variable++

# bzw.
++a_variable

Hier ein Link dazu: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/52956

Integer-Arithmetik bei negativen Zahlen

...verhält sich anders, als von Sprachen wie C/C++, PHP oder Java gewohnt.

1
2
3
puts 10/9      #>> 1, OK
puts -10/9     #>> -2, Ups -> -1 erwartet
puts -10 % 9   #>>  8, Ups -> -1 erwartet

Mathematisch gesehen ist dieses Verhalten aber korrekt. Auch Python und TCL verhalten sich da genau so. Es muss immer die floor-Bedingung erfüllt sein: "Der größte Integerwert der aber kleiner-gleich dem Float-Ergebnis ist."

1
2
3
4
5
6
7
8
puts  1.2.floor #>> 1
puts  1.0.floor #>> 1
puts -1.0.floor #>> -1
puts -1.2.floor #>> -2

#aber alternativ:
puts  1.2.to_i  #>>  1
puts -1.2.to_i  #>> -1

Also ist

-10/9 = 10/-9 = -2 , da   -2 < (-10.0/9.0=10.0/-9.0=-1,111) < -1.

Modulo:

a modulo b = Rest   =>   a = a/b * b + Rest , mit a/b = (a.to_f / b.to_f).floor

a modulo b = Rest  =  a - a/b * b

-10 % 9  =  -10 - -2 * 9  =  -10 + 18  =  8

10 % -9  =   10 - -2 * -9  =  10 - 18  = -8

Für Umsteiger gibt es neben % und divmod() noch remainder(), welches sich wie modulo in C/C++ verhält.

Weblinks:

0 in Ruby ist true

In Ruby sollte man mit true und false/nil arbeiten. 0 ist als Bedingung wahr, nicht wie z.B. in C/C++ falsch.

1
2
3
4
5
6
7
8
9
10
a = 0
while a    # -> 0 ist wahr
  #...
end

while 1    # -> true, besser aber gleich true verwenden

while true # guter Stil

loop do    # statt "while true"

Leerstellen machen den Unterschied

Ein Beispiel, bei dem eine Leerstelle das Ergebnis verändert:

1
2
3
4
5
6
7
8
  class Fixnum
    def -@
      'I shot the sheriff.'
    end
  end
  
  puts(-5)  #-> -5
  puts(- 5)  #-> I shot the sheriff.

Der Grund liegt in der Art, wie Ruby den Code zerlegt: -5 wird als Konstante mit dem Wert -5 gelesen, - 5 jedoch als Aufruf der Methode -@ der Konstante 5.

Anmerkung: Man sollte niemals die internen mathematischen Funktionen überschreiben, es sei denn, man braucht ein pathologisches Beispiel.

Deep Copies und Referenzen

Als typische OO-Sprache ist (fast) alles in Ruby ein Objekt? - Nein! Eine Referenz auf ein Objekt!

1
2
3
4
  x=[1,2,3,4]
  y=x
  y[0]=5
  puts x[0] #==> 5

Der Zuweisungsoperator "=" bezieht sich nur auf die Referenz. Genauso läuft das mit Übergaben an Argumente in Methoden:

1
2
3
4
5
6
7
8
9
  def f(x)
    x[0]=1
    return x
  end
  
  x=Array.new(2)
  y=f(x)
  y[0]=2
  puts x[0] #==> 2, da x als Referenz ��bergeben und zur��ckgegeben wurde

Im Allgemeinen gibt es zwei Verfahren für "Deep Copies":

  • Echtes Kopieren der Objekte, nicht der Referenzen: Man kopiert den Inhalt von Objekt 2 in ein schon vorhandenes Objekt 1.
  • Oder man erzeugt ein neues Objekt mit dem Inhalt von Objekt 2.

Bei Sprachen mit Memory Management wie Ruby oder Java wird meistens nur das letztere Verfahren realisiert. Bei Deep Copies entsteht also immer eine neue Referenz!

Die allgemeinen Methoden für Deep Copies sind Methoden der Klasse Object und heissen dup() und clone(). Sie kann man auf alles anwenden: Strings, Arrays, Hashes usw. Was auch geht, was aber nicht so verständlich im Code ist:

object1 = Object.new(object2)


Darüberhinaus kann man jede Methode "missbrauchen", die ein neu erzeugtes Objekt zurückgibt, z.B. bei Strings:

1
2
3
4
5
s2.replace s1 #==> Ausnahme: Hier ensteht KEINE neue Referenz

# oder

s2=s1*1

"Call by reference", "Call by value" und mehrere Rückgabewerte bei Methoden

Wer von anderen, "alten" Sprachen wie C++ oder Pascal/Delphi kommt, der wird gewohnt sein, manchmal mehr als einen Funktionswert via Call by reference - Argumente zurückzugeben. Nun liest man überall, dass Ruby ebenfalls "Call by reference" macht. Also muss das ja klappen:

1
2
3
4
5
6
7
8
9
10
11
def f(s1,s2)
  # s2 soll zur��ckgegeben werden
  s2='123'
  return s2.size() #==> Gr��sse von s2 soll zur��ckgegeben werden
end

s1='x'
s2=''
n=f(s1,s2)
puts n #==> 3
puts s2 #==> Leerstring

Worin liegt das Problem? Nun, bei "s2='123'" ist eine neue Referenz erzeugt worden. Die Referenzen im Argument von f() können aber nicht verändert werden. Insofern pflegt Ruby, was Referenzen anbelangt, "Call by value"! s2 zeigt nach dem Aufruf unverändert auf den alten - leeren - String.

Es gibt für einige Klassen Methoden die nur den inhalt austauschen, ohne das Objekt neu zu zuweisen.

1
2
3
4
5
6
7
8
9
10
11
def f(s1,s2)
  # s2 soll zur��ckgegeben werden
  s2.replace('123')
  return s2.size() #==> Gr��sse von s2 soll zur��ckgegeben werden
end

s1='x'
s2=''
n=f(s1,s2)
puts n #==> 3
puts s2 #==> '123'


Was tun, wenn man mehrere Werte zurückgeben will? Nun, gar kein Problem, Ruby unterstützt das. Einfach eine Liste der Werte zurückgeben:

1
2
3
4
5
6
7
8
9
def f(s1,s2)
  s2='123'
  return s2, s2.size() 
end

s1='x'
s2, n=f(s1,s2)
puts n #==> 3
puts s2 #==> 123


Vergleiche mit ==

In Ruby werden Vergleiche mit dem ==-Operator ausgeführt, nicht mit =. Das führt oft zu unerwarteten Ergebnissen:

1
2
3
4
5
6
7
8
9
10
x = 2
if x = 1 then
  #Sollte ja nicht eintreten, oder?
  puts "x war 1!"
end
puts x
#---------------------------------------
#Output
#=> x war 1!
#=> 1

Was ist passiert? = hat eine Zuweisung ausgelöst und so der Variablen x den Wert 1 zugewiesen. Da 1 als true gilt (wie auch 0 und alle anderen Objekte außer false und nil), ist die Bedingung erfüllt. Wird = mit == ersetzt, ist alles in Butter:

1
2
3
4
5
6
7
8
9
x = 2
if x == 1 then
  #Tritt nicht ein. 
  puts "x war 1!"
end
puts x
#---------------------------------------
#Output
#=> 2

Dateipfade auf Windows

Windows verwendet ja bekannterweise Backslashes zum Trennen von Pfadelementen, z.B. C:\Dokumente und Einstellungen\Ich\Eigene Dateien. Ruby interpretiert Backslashes in normalen Strings mit doppelten Anführungszeichen als Sonderzeichensequenzen. Daher sollte man besser normale Slashes / verwenden, sie werden von Ruby automatisch umgewandelt. Alternativ lassen sich auch doppelte Backslashes \\ verwenden, aber diese sind nicht über verschiedene Plattformen hinweg kompatibel.

1
2
3
my_path = "C:/Dokumente und Einstellungen/Ich/Eigene Dateien"
#oder, aber nicht empfehlenswert
my_path = "C:\\Dokumente und Einstellungen\\Ich\\Eigene Dateien"


$: enthält das aktuelle Verzeichnis nicht

Seit Ruby 1.9.2 enthält die spezielle globale Variable $: das aktuelle Arbeitsverzeichnis (».«) nicht mehr. Das bedeutet, dass Skripte, die im gleichen Verzeichnis liegen wie das ausführende Skript, nicht von require gefunden werden. Lösung: Statt require, welches seit 1.9.2 nur noch zum Laden von Bibliotheken aus System-Installationsorten zuständig ist, require_relative verwenden.

Beispiel: Wir haben folgende Dateistruktur:

1
2
3
4
5
/
  - mein_skript.rb
  - mein_zweites_skript.rb
  mein_ordner/
    - mein_drittes_skript.rb

Will mein_skript.rb die zwei anderen Skripte per require einbinden, so muss es so aussehen:

1
2
3
#mein_skript.rb
require_relative "mein_zweites_skript"
require_relative "mein_order/mein_drittes_skript"


Um die Kompatibilität mit Ruby 1.8 zu wahren, kann man auch ».« zu $: hinzufügen, was aber nicht gern gesehen wird:

1
2
3
4
#mein_skript.rb mit 1.8-Kompatibilit��t
$:.unshift(".") #unshift, weil in 1.8 . ganz vorn war
require "mein_zweites_skript"
require "mein_ordner/mein_drittes_skript"


Außerdem hat require_relative den Vorteil, dass es auch funktioniert, wenn das aktuelle Arbeitsverzeichnis nicht das ist, in dem das Skript liegt.

Siehe auch