--- language: Crystal contributors: - ["Vitalii Elenhaupt", "http://veelenga.com"] - ["Arnaud Fernandés", "https://github.com/TechMagister/"] translators: - ["caminsha", "https://github.com/caminsha"] filename: learncrystal-de.cr --- ```crystal # Das ist ein Kommentar # Alles ist ein Objekt nil.class # => Nil 100.class # => Int32 true.class # => Bool # Falschwerte sind: nil, false und Nullpointer !nil # => true : Bool !false # => true : Bool !0 # => false : Bool # Integer 1.class # => Int32 # Fünf vorzeichenbehaftete Ganzzahlen 1_i8.class # => Int8 1_i16.class # => Int16 1_i32.class # => Int32 1_i64.class # => Int64 1_i128.class # => Int128 # Fünf vorzeichenlose Ganzzahlen 1_u8.class # => UInt8 1_u16.class # => UInt16 1_u32.class # => UInt32 1_u64.class # => UInt64 1_u128.class # => UInt128 2147483648.class # => Int64 9223372036854775808.class # => UInt64 # Binäre Zahlen 0b1101 # => 13 : Int32 # Oktalzahlen 0o123 # => 83 : Int32 # Hexadezimalzahlen 0xFE012D # => 16646445 : Int32 0xfe012d # => 16646445 : Int32 # Gleitkommazahlen (floats) 1.0.class # => Float64 # Es gibt zwei Typen von Gleitkommazahlen 1.0_f32.class # => Float32 1_f32.class # => Float32 1e10.class # => Float64 1.5e10.class # => Float64 1.5e-7.class # => Float64 # Chars (einzelne Zeichen) 'a'.class # => Char # Oktale Schreibweise '\101' # => 'A' : Char # Unicode Schreibweise '\u0041' # => 'A' : Char # Strings (Zeichenketten) "s".class # => String # Strings sind unveränderlich s = "hello, " # => "hello, " : String s.object_id # => 1234667712 : UInt64 s += "Crystal" # => "hello, Crystal" : String s.object_id # => 142528472 : UInt64 # Interpolation wird unterstützt "sum = #{1 + 2}" # => "sum = 3" : String # Mehrzeilige Strings " Dies ist ein mehrzeiliger String." # String mit doppeltem Anführungszeichen %(hello "world") # => "hello \"world\"" # Symbole # Unveränderbare, wiederverwendbare Konstanten, welche intern als Int32 Integer # Werte repräsentiert werden. # Symbole werden oft anstelle von Strings verwendet, um bestimmte Werte zu bestimmen. :symbol.class # => Symbol sentence = :question? # :"question?" : Symbol sentence = :question? # => true : Bool sentence = :exclamation! # => false : Bool sentence = "question?" # => false : Bool # Arrays [1, 2, 3].class # => Array(Int32) [1, "hello", 'x'].class # => Array(Int32 | String | Char) # Leere Arrays sollten einen Typen definieren [] # => Syntaxfehler: für leere Arrays, # verwende `[] of ElementType` [] of Int32 # => [] : Array(Int32) Array(Int32).new # => [] : Array(Int32) # Arrays können indiziert werden array = [1, 2, 3, 4, 5] # => [1, 2, 3, 4, 5] : Array(Int32) array[0] # => 1 : Int32 array[10] # führt zu einem IndexError array[-6] # führt zu einem IndexError array[10]? # => nil : (Int32 | Nil) array[-6]? # => nil : (Int32 | Nil) # Starte am Ende des Arrays array[-1] # => 5 # Mit einem Startindex und einer Länge array[2, 4] # => [3, 4, 5] # oder mit einem Bereich array[1..3] # => [2, 3, 4] # Füge etwas zu einem Array hinzu array << 6 # => [1, 2, 3, 4, 5, 6] # Entferne Einträge am Ende des Arrays array.pop # => 6 array # => [1, 2, 3, 4, 5] # Entferne ersten Eintrag im Array array.shift # => 1 array # => [2, 3, 4, 5] # Überprüfe, ob ein Element in einem Array existiert array.includes? 3 # => true # Spezielle Syntax für String-Arrays und Symbol-Arrays %w(one two three) # => ["one", "two", "three"] : Array(String) %i(one two three) # 0> [:one, :two, :three] : Array(Symbol) # Es gibt auch für andere Arrays eine spezielle Syntax, wenn die Methoden # `.new` und `#<<` definiert werden. set = Set{1, 2, 3} # => [1, 2, 3] set.class # => Set(Int32) # Das obere ist äquivalent zu: set = Set(typeof(1, 2, 3)).new set << 1 set << 2 set << 3 # Hashes {1 => 2, 3 => 4}.class # => Hash(Int32, Int32) {1 => 2, 'a' => 3}.class # => Hash (Int32 | Char, Int32) # Leere Hashes sollten einen Typen spezifizieren {} # Syntaxfehler {} of Int32 => Int32 # {} Hash(Int32, Int32).new # {} # Hashes können schnell mit dem Key nachgeschaut werden hash = {"color" => "green", "number" => 5} hash["color"] # => "green" hash["no_such_key"] # => Fehlender hash key: "no_such_key" (KeyError) hash["no_such_key"]? # => nil # Überprüfe die Existenz eines Hashkeys hash.has_key? "color" # => true # Spezielle Schreibweise für Symbol- und Stringkeys {key1: 'a', key2: 'b'} # {:key1 => 'a', :key2 => 'b'} {"key1": 'a', "key2": 'b'} # {"key1" => 'a', "key2" => 'b'} # Die spezielle Syntax für Hash-Literale gibt es auch für andere Typen, sofern # diese die Methoden `.new` und `#[]=` Methoden definieren. class MyType def []=(key, value) puts "do stuff" end end MyType{"foo" => "bar"} # Das obere ist äquivalent zu: tmp = MyType.new tmp["foo"] = "bar" tmp # Ranges (Bereiche) 1..10 # => Range(Int32, Int32) Range.new(1,10).class # => Range(Int32, Int32) # Ranges können inklusiv oder exklusiv sein. (3..5).to_a # => [3, 4, 5] (3...5).to_a # => [3, 4] # Überprüfe, ob ein Range einen Wert enthält oder nicht. (1..8).includes? 2 # => true # Tupel sind unveränderliche, Stack-zugewiese Folgen von Werten mit fester # Größe und möglicherweise unterschiedlichen Typen {1, "hello", 'x'}.class # => Tuple(Int32, String, Char) # Erhalte den Wert eines Tupels über den Index tuple = {:key1, :key2} tuple[1] # => :key2 tuple[2] # syntax error: Index out of bound # Können auf mehrere Variablen erweitert werden a, b, c = {:a, 'b', "c"} a # => :a b # => 'b' c # => "c" # Procs repräsentieren ein Funktionspointer mit einem optionalen Kontext. # Normalerweise wird ein Proc mit einem proc-Literal erstellt. proc = ->(x : Int32) { x.to_s } proc.class # => Print(Int32, String) # Außerdem kann man auch mit der Methode `new` ein Proc erstellen. Proc(Int32, String).new { |x| x.to_s } # Rufe ein Proc auf mit der Methode `call` proc.call 10 # => "10" # Kontrollstatements if true "if statement" elsif false "else-f, optional" else "else, auch optional" end puts "if as a suffix" if true # => if as a suffix # If als Ausdruck a = if 2 > 1 3 else 4 end a # => 3 # Bedingter ternärer Ausdruck a = 1 > 2 ? 3 : 4 # => 4 # Case-Statement cmd = "move" action = case cmd when "create" "Creating..." when "copy" "Copying..." when "move" "Moving..." when "delete" "Deleting..." end action # => "Moving..." # Schleifen index = 0 while index <= 3 puts "Index: #{index}" index += 1 end # Index: 0 # Index: 1 # Index: 2 # Index: 3 index = 0 until index > 3 puts "Index: #{index}" index += 1 end # Index: 0 # Index: 1 # Index: 2 # Index: 3 # Der bevorzugte Weg, ist `each` zu verwenden. (1..3).each do |index| puts "Index: #{index}" end # Index: 1 # Index: 2 # Index: 3 # Der Typ der Variablen hängt vom Typen innerhalb der Kontrollanweisung ab if a < 3 a = "hello" else a = true end typeof a # => (Bool | String) if a && b # Hier wird garantiert, dass weder a noch b vom Typ Nil sind end if a.is_a? String a.class # => String end # Funktionen def double(x) x * 2 end # Funktionen geben implizit den Wert der letzten Anweisung zurück # Dies ist auch bei anderen Blöcken der Fall. double(2) # => 4 # Klammern müssen nicht gesetzt werden, wenn der Aufruf eindeutig ist double 3 # => 6 double double 3 # => 12 def sum(x, y) x + y end # Funktionsargumente werden mit einem Komma separiert. sum 3, 4 # => 7 sum sum(3, 4), 5 # => 12 # yield # Alle Methoden haben einen impliziten, optionalen Blockparameter. # Dieser kann mit dem Schlüsselwort `yield` aufgerufen werden. def surround puts '{' yield puts '}' end surround { puts "Hallo Welt" } # { # Hallo Welt # } # Du kannst ein Block einer Funktion übergeben. # "&" kennzeichnet eine Referenz zu einem übergebenen Block def guests(&block) block.call "some_argument" end # Du kannst eine Liste von Argumenten mitgeben, welche zu einem Array # umgewandelt werden. # Hierfür ist der Splat-Operator ("*") def guests(*array) array.each { |guest| puts guest } end # Wenn eine Methode ein Array zurückgibt, kann destrukturiende Zuordnung # verwendet werden. def foods ["pancake", "sandwich", "quesadilla"] end breakfast, lunch, dinner = foods breakfast # => "pancake" dinner # => "quesadilla" # Gemäß der Konvention enden alle Methoden, welchen einen Boolean zurückgeben # mit einem Fragezeichen. 5.even? # false 5.odd? # true # Und wenn eine Methode mit einem Ausrufezeichen endet, macht sie etwas # destruktives. Zum Beispiel wird der Aufrufer verändert. Einige Methoden haben # eine !-Version, um eine Änderung zu machen und eine Nicht-!-Version, welche # lediglich eine neue veränderte Version zurückgibt. company_name = "Dunder Mifflin" company_name.gsub "Dunder", "Donald" # => "Donald Mifflin" company_name # => "Dunder Mifflin" company_name.gsub! "Dunder", "Donald" company_name # => "Donald Mifflin" # definiere eine Klasse mit dem Schlüsselwort `class`. class Human # eine Klassenvariable. Diese wird mit allen Instanzen dieser Klasse geteilt. @@species = "H. sapiens" # type of name is String @name: String # Grundlegender Intialisierer # Weise das Argument der Instanz-Variable "name" zu # Wenn kein Alter angegeben wird, wird der Default (hier 0) genommen. def initialize(@name, @age = 0) end # Einfache Setter-Methode def name=(name) @name = name end # einfache Getter-Methode def name @name end # Die obere Funktionalität kann mit der property-Methode gekapselt werden: property :name # Getter/Setter-Methoden können auch individuell erstellt werden: getter :name setter :name # eine Klassenmethode verwendet `self` um sich von Instanzmethoden zu # unterscheiden. Diese kann lediglich von einer Klasse aufgerufen werden, # nicht von einer Instanz. def self.say(msg) puts msg end def species @@species end end # Eine Klasse instanziieren jim = Human.new("Jim Halpert") dwight = Human.new("Dwight K. Schrute") # Lass uns ein paar Methoden aufrufen jim.species # => "H. sapiens" jim.name # => "Jim Halpert" jim.name = "Jim Halpert II" # => "Jim Halpert II" jim.name # => "Jim Halpert II" dwight.species # => "H. sapiens" dwight.name # => "Dwight K. Schrute" # Rufe die Klassenmethode auf Human.say("Hi") # => gibt Hi aus und gibt `nil` zurück # Variablen, welche mit @ starten, sind im Scope der Instanz class TestClass @var = "Ich bin eine Instanzvariable" end # Variablen, welche mit @@ starten, sind im Scope der Klasse class TestClass @@var = "Ich bin eine Klassenvariable" end # Variablen, welche mit einem Großbuchstaben starten, sind Konstanten. Var = "Ich bin eine Konstante" Var = "Ich kann nicht aktualisiert werden." # Die Konstante Var wurde bereits # initialisiert. # In Crystal ist Class auch ein Objekt. Dadurch können Klassen Instanzvariablen # haben. Klassenvariablen werden mit der Klasse und allen Subklassen geteilt. # Basisklasse class Human @@foo = 0 def self.foo @@foo end def self.foo=(value) @@foo = value end end # abgeleitete Klasse class Worker < Human end Human.foo # => 0 Worker.foo # => 0 Human.foo = 2 # => 2 Worker.foo # => 0 Worker.foo = 3 # => 3 Human.foo # => 2 Worker.foo # => 3 module ModuleExample def foo "foo" end end # Wenn ein Modul mit include eingeschlossen wird, so werden die Methoden an die # Instanzen gebunden. # Wenn eine Klasse mit einem Modul erweitert wird, so werden die Methoden an die # Klasse selbst gebunden. class Person include ModuleExample end class Book extend ModuleExample end Person.foo # => undefinierte Methode 'foo' für Person:Class Person.new.foo # => 'foo' Book.foo # => 'foo' Book.new.foo # => undefinierte Methode für Book # Ausnahmebehandlung # Definiere eine neue Ausnahme class MyException < Exception end # Definiere eine weitere Ausnahme class MyAnotherException < Exception; end ex = begin raise MyException.new rescue ex1 : IndexError "ex1" rescue ex2 : MyException | MyAnotherException "ex2" rescue ex3 : Exception "ex3" rescue ex4 # fange alle Ausnahmen ab "ex4" end ex # => "ex2" ``` ## Weitere Unterlagen - [offizielle Dokumentation, englisch](https://crystal-lang.org/)