ÜbungBaut auf Katas: Dictionaries auf
icon: bi-boxes kind: uebung duration: 2 Doppelstunden languages: [ruby, python, javascript] idea: Klassen schreiben, Objekte bauen, Zustand hüten und vererben curriculum: "Sek I: Algorithmisches Problemlösen (Grundlagen der Objektorientierung); Oberstufe: objektorientierte Programmierung (Klassen, Objekte, Attribute, Methoden, Vererbung)" topics: [objektorientierung] requires: [kata-dicts]

Katas: Objekte

Du schreibst Klassen und baust daraus Objekte: jedes mit seinen eigenen Daten und mit Methoden, die damit arbeiten. Ein Objekt passt auf seinen Zustand auf, kennt andere Objekte, zeigt sich als Text und erbt von einer Klasse, was es nicht selbst anders macht.

1. Eine Klasse, viele Objekte

Eine Klasse ist ein Bauplan. Aus ihm baut man Objekte, so viele man will, und jedes hat seine eigenen Daten, die Attribute. Was ein Objekt kann, steht in seinen Methoden. Dieser Zähler fängt bei 0 an und zählt bei jedem increment eins weiter:

class Counter
  def initialize
    @value = 0
  end

  def increment
    @value += 1
  end

  def value
    @value
  end
end

clicks = Counter.new
clicks.increment
puts clicks.value    # 1

initialize läuft, wenn Counter.new ein neues Objekt baut. Ein Name mit @ davor ist ein Attribut: Er gehört dem Objekt, nicht der Methode, und bleibt, wenn die Methode fertig ist.

class Counter:
    def __init__(self):
        self.value = 0

    def increment(self):
        self.value += 1

clicks = Counter()
clicks.increment()
print(clicks.value)    # 1

__init__ läuft, wenn Counter() ein neues Objekt baut. self ist das Objekt selbst; self.value ist ein Attribut: Es gehört dem Objekt, nicht der Methode, und bleibt, wenn die Methode fertig ist. Jede Methode bekommt self als ersten Parameter, beim Aufruf schreibt man es nicht hin.

class Counter {
  constructor() {
    this.value = 0;
  }

  increment() {
    this.value += 1;
  }
}

const clicks = new Counter();
clicks.increment();
console.log(clicks.value);    // 1

constructor läuft, wenn new Counter() ein neues Objekt baut. this ist das Objekt selbst; this.value ist ein Attribut: Es gehört dem Objekt, nicht der Methode, und bleibt, wenn die Methode fertig ist.

Vermutung 1Zwei ZählerEinstieg

Was gibt die letzte Zeile aus?

a = Counter.new
b = Counter.new
a.increment
a.increment
b.increment
puts a.value
Vermutung 1Zwei ZählerEinstieg

Was gibt die letzte Zeile aus?

a = Counter()
b = Counter()
a.increment()
a.increment()
b.increment()
print(a.value)
Vermutung 1Zwei ZählerEinstieg

Was gibt die letzte Zeile aus?

const a = new Counter();
const b = new Counter();
a.increment();
a.increment();
b.increment();
console.log(a.value);

Hier geht es weiter, sobald du deine Antwort oben abgegeben hast.

a zählt bis 2, b bis 1: Jedes Objekt hat sein eigenes Attribut value. Die Klasse gibt nur vor, dass es eins gibt; welchen Wert es hat, steht in jedem Objekt für sich.

Methoden können auch Parameter haben, wie Funktionen. Das Objekt bekommt seine Daten beim Bauen mit:

class Circle
  def initialize(radius)
    @radius = radius
  end

  def diameter
    2 * @radius
  end
end

puts Circle.new(5).diameter    # 10
class Circle:
    def __init__(self, radius):
        self.radius = radius

    def diameter(self):
        return 2 * self.radius

print(Circle(5).diameter())    # 10
class Circle {
  constructor(radius) {
    this.radius = radius;
  }

  diameter() {
    return 2 * this.radius;
  }
}

console.log(new Circle(5).diameter());    // 10
Aufgabe 2Ein RechteckEinstieg

Schreib eine Klasse Rectangle, deren Objekte mit Breite und Höhe gebaut werden, Rectangle.new(3, 4), und die zwei Methoden hat: area gibt die Fläche zurück (12), perimeter den Umfang (14). Die Prüfung baut auch zwei Rechtecke nebeneinander.

Tipp

initialize bekommt Breite und Höhe, aber die Parameter sind weg, wenn initialize fertig ist. Was muss das Objekt sich merken, damit area später daran kommt?

Nächster Tipp

Schreib in initialize @width = width und @height = height. Dann können area und perimeter mit @width und @height rechnen.

Aufgabe 2Ein RechteckEinstieg

Schreib eine Klasse Rectangle, deren Objekte mit Breite und Höhe gebaut werden, Rectangle(3, 4), und die zwei Methoden hat: area() gibt die Fläche zurück (12), perimeter() den Umfang (14). Die Prüfung baut auch zwei Rechtecke nebeneinander.

Tipp

__init__ bekommt Breite und Höhe, aber die Parameter sind weg, wenn __init__ fertig ist. Was muss das Objekt sich merken, damit area später daran kommt?

Nächster Tipp

Schreib in __init__ self.width = width und self.height = height. Dann können area und perimeter mit self.width und self.height rechnen.

Aufgabe 2Ein RechteckEinstieg

Schreib eine Klasse Rectangle, deren Objekte mit Breite und Höhe gebaut werden, new Rectangle(3, 4), und die zwei Methoden hat: area() gibt die Fläche zurück (12), perimeter() den Umfang (14). Die Prüfung baut auch zwei Rechtecke nebeneinander.

Tipp

Der constructor bekommt Breite und Höhe, aber die Parameter sind weg, wenn er fertig ist. Was muss das Objekt sich merken, damit area später daran kommt?

Nächster Tipp

Schreib im constructor this.width = width; und this.height = height;. Dann können area und perimeter mit this.width und this.height rechnen.

2. Ein Objekt passt auf sich auf

Ein Objekt ändert seine Attribute nur in seinen Methoden. Darum kann es Regeln durchsetzen: Ein Konto lässt sich nicht ins Minus bringen, wenn die Methode, die abhebt, vorher nachsieht. Wer das Konto benutzt, muss die Regel nicht kennen; er ruft die Methode auf, und das Objekt entscheidet. Das heißt Kapselung.

Aufgabe 3Ein Konto, das nicht ins Minus gehtÜbung

Schreib eine Klasse Account für ein Konto, das mit einem Kontostand gebaut wird, Account.new(10). deposit(amount) zahlt ein, balance gibt den Kontostand zurück. withdraw(amount) hebt ab und gibt true zurück, aber nur, wenn genug da ist; sonst bleibt der Kontostand, wie er ist, und withdraw gibt false zurück.

Tipp

Das Konto merkt sich seinen Stand in einem Attribut, @balance. Alle vier Methoden arbeiten damit.

Nächster Tipp

In withdraw kommt zuerst die Frage, ob amount größer ist als der Kontostand. Wenn ja: return false, ohne etwas zu ändern.

Nächster Tipp

Sonst ziehst du ab und gibst true zurück: @balance -= amount, dann true.

Aufgabe 3Ein Konto, das nicht ins Minus gehtÜbung

Schreib eine Klasse Account für ein Konto, das mit einem Kontostand gebaut wird, Account(10). deposit(amount) zahlt ein, balance() gibt den Kontostand zurück. withdraw(amount) hebt ab und gibt True zurück, aber nur, wenn genug da ist; sonst bleibt der Kontostand, wie er ist, und withdraw gibt False zurück.

Tipp

Das Konto merkt sich seinen Stand in einem Attribut. Nenn es nicht self.balance, denn so heißt schon die Methode; self.amount oder self.money geht.

Nächster Tipp

In withdraw kommt zuerst die Frage, ob amount größer ist als der Kontostand. Wenn ja: return False, ohne etwas zu ändern.

Nächster Tipp

Sonst ziehst du ab und gibst True zurück.

Aufgabe 3Ein Konto, das nicht ins Minus gehtÜbung

Schreib eine Klasse Account für ein Konto, das mit einem Kontostand gebaut wird, new Account(10). deposit(amount) zahlt ein, balance() gibt den Kontostand zurück. withdraw(amount) hebt ab und gibt true zurück, aber nur, wenn genug da ist; sonst bleibt der Kontostand, wie er ist, und withdraw gibt false zurück.

Tipp

Das Konto merkt sich seinen Stand in einem Attribut. Nenn es nicht this.balance, denn so heißt schon die Methode; this.money geht.

Nächster Tipp

In withdraw kommt zuerst die Frage, ob amount größer ist als der Kontostand. Wenn ja: return false;, ohne etwas zu ändern.

Nächster Tipp

Sonst ziehst du ab und gibst true zurück.

3. Objekte, die Objekte kennen

Ein Attribut kann auch ein anderes Objekt sein, oder eine Liste von Objekten. Eine Playlist kennt ihre Songs, und jeder Song kennt seinen Titel und seine Länge:

class Song
  attr_reader :title, :seconds

  def initialize(title, seconds)
    @title = title
    @seconds = seconds
  end
end

class Playlist
  def initialize
    @songs = []
  end

  def add(song)
    @songs << song
  end

  def count
    @songs.length
  end
end

mix = Playlist.new
mix.add(Song.new("Intro", 95))
mix.add(Song.new("Outro", 210))
puts mix.count    # 2

attr_reader :title, :seconds schreibt die Methoden title und seconds, die nur den Wert des Attributs zurückgeben, damit man von außen song.title fragen kann.

class Song:
    def __init__(self, title, seconds):
        self.title = title
        self.seconds = seconds

class Playlist:
    def __init__(self):
        self.songs = []

    def add(self, song):
        self.songs.append(song)

    def count(self):
        return len(self.songs)

mix = Playlist()
mix.add(Song("Intro", 95))
mix.add(Song("Outro", 210))
print(mix.count())    # 2
class Song {
  constructor(title, seconds) {
    this.title = title;
    this.seconds = seconds;
  }
}

class Playlist {
  constructor() {
    this.songs = [];
  }

  add(song) {
    this.songs.push(song);
  }

  count() {
    return this.songs.length;
  }
}

const mix = new Playlist();
mix.add(new Song("Intro", 95));
mix.add(new Song("Outro", 210));
console.log(mix.count());    // 2
Aufgabe 4Die Playlist rechnetÜbung

Die Klassen Song und Playlist von oben stehen schon da. Schreib in Playlist zwei Methoden: total_seconds gibt zurück, wie lange alle Songs zusammen dauern, und longest_title den Titel des längsten Songs; dauern zwei gleich lang, den, der zuerst kam. In einer leeren Playlist ergibt total_seconds 0 und longest_title nil.

Tipp

Beide Methoden gehen durch @songs. Jeder Song kennt seine Länge, song.seconds, und seinen Titel, song.title.

Nächster Tipp

Für total_seconds zählst du alle song.seconds zusammen, mit einer Schleife oder @songs.sum { |song| song.seconds }.

Nächster Tipp

Für longest_title merkst du dir den längsten Song bisher, anfangs nil, und tauschst ihn nur, wenn ein Song länger ist. Am Ende gibst du seinen Titel zurück, oder nil, wenn es keinen gab.

Aufgabe 4Die Playlist rechnetÜbung

Die Klassen Song und Playlist von oben stehen schon da. Schreib in Playlist zwei Methoden: total_seconds() gibt zurück, wie lange alle Songs zusammen dauern, und longest_title() den Titel des längsten Songs; dauern zwei gleich lang, den, der zuerst kam. In einer leeren Playlist ergibt total_seconds() 0 und longest_title() None.

Tipp

Beide Methoden gehen durch self.songs. Jeder Song kennt seine Länge, song.seconds, und seinen Titel, song.title.

Nächster Tipp

Für total_seconds zählst du alle song.seconds zusammen, mit einer Schleife oder sum(song.seconds for song in self.songs).

Nächster Tipp

Für longest_title merkst du dir den längsten Song bisher, anfangs None, und tauschst ihn nur, wenn ein Song länger ist. Am Ende gibst du seinen Titel zurück, oder None, wenn es keinen gab.

Aufgabe 4Die Playlist rechnetÜbung

Die Klassen Song und Playlist von oben stehen schon da. Schreib in Playlist zwei Methoden: totalSeconds() gibt zurück, wie lange alle Songs zusammen dauern, und longestTitle() den Titel des längsten Songs; dauern zwei gleich lang, den, der zuerst kam. In einer leeren Playlist ergibt totalSeconds() 0 und longestTitle() null.

Tipp

Beide Methoden gehen durch this.songs. Jeder Song kennt seine Länge, song.seconds, und seinen Titel, song.title.

Nächster Tipp

Für totalSeconds zählst du alle song.seconds zusammen, mit einer Schleife oder mit reduce.

Nächster Tipp

Für longestTitle merkst du dir den längsten Song bisher, anfangs null, und tauschst ihn nur, wenn ein Song länger ist. Am Ende gibst du seinen Titel zurück, oder null, wenn es keinen gab.

4. Wie ein Objekt sich zeigt

Macht man aus einem Objekt einfach Text, steht da wenig: in Ruby und Python die Klasse und wo das Objekt im Speicher liegt, in JavaScript nur [object Object]. Was ein Mensch lesen soll, bestimmt eine Methode mit einem festen Namen:

class Point
  def initialize(x, y)
    @x = x
    @y = y
  end

  def to_s
    "(#{@x}, #{@y})"
  end
end

puts Point.new(3, 4)    # (3, 4)

puts ruft to_s auf, und "Punkt #{p}" auch.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"({self.x}, {self.y})"

print(Point(3, 4))    # (3, 4)

print und str(p) rufen __str__ auf, und f"Punkt {p}" auch.

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return `(${this.x}, ${this.y})`;
  }
}

console.log(`${new Point(3, 4)}`);    // (3, 4)

String(p) und `Punkt ${p}` rufen toString auf. console.log(p) allein zeigt dagegen die Attribute, nicht toString.

Aufgabe 5Geld als TextÜbung

Schreib eine Klasse Money, deren Objekte mit Euro und Cent gebaut werden, Money.new(3, 5), und die sich als Text so zeigt: 3,05 €. Die Cent haben immer zwei Ziffern. Die Prüfung ruft to_s auf und setzt ein Objekt auch in einen Text ein.

Tipp

Merk dir Euro und Cent in zwei Attributen und schreib eine Methode to_s, die einen Text zurückgibt.

Nächster Tipp

Die Cent bekommen mit format("%02d", @cents) zwei Ziffern, mit einer 0 vorn, wo nötig.

Nächster Tipp

"#{@euros},#{format("%02d", @cents)} €" setzt alles zusammen.

Aufgabe 5Geld als TextÜbung

Schreib eine Klasse Money, deren Objekte mit Euro und Cent gebaut werden, Money(3, 5), und die sich als Text so zeigt: 3,05 €. Die Cent haben immer zwei Ziffern. Die Prüfung ruft str() auf und setzt ein Objekt auch in einen f-String ein.

Tipp

Merk dir Euro und Cent in zwei Attributen und schreib eine Methode __str__, die einen Text zurückgibt.

Nächster Tipp

Die Cent bekommen mit f"{self.cents:02d}" zwei Ziffern, mit einer 0 vorn, wo nötig.

Nächster Tipp

f"{self.euros},{self.cents:02d} €" setzt alles zusammen.

Aufgabe 5Geld als TextÜbung

Schreib eine Klasse Money, deren Objekte mit Euro und Cent gebaut werden, new Money(3, 5), und die sich als Text so zeigt: 3,05 €. Die Cent haben immer zwei Ziffern. Die Prüfung ruft String() auf und setzt ein Objekt auch in einen Template-String ein.

Tipp

Merk dir Euro und Cent in zwei Attributen und schreib eine Methode toString(), die einen Text zurückgibt.

Nächster Tipp

Die Cent bekommen mit String(this.cents).padStart(2, "0") zwei Ziffern, mit einer 0 vorn, wo nötig.

Nächster Tipp

`${this.euros},${String(this.cents).padStart(2, "0")} €` setzt alles zusammen.

5. Vererbung

Manche Klassen sind fast gleich. Statt alles zweimal zu schreiben, erbt eine Klasse von einer anderen: Sie bekommt deren Attribute und Methoden und schreibt nur, was bei ihr anders ist. Diese Methode heißt dann überschrieben:

class Animal
  def initialize(name)
    @name = name
  end

  def sound
    "..."
  end

  def describe
    "#{@name} sagt #{sound}"
  end
end

class Dog < Animal
  def sound
    "Wuff"
  end
end

class Cat < Animal
  def sound
    "Miau"
  end
end

class Dog < Animal heißt: Dog erbt von Animal.

class Animal:
    def __init__(self, name):
        self.name = name

    def sound(self):
        return "..."

    def describe(self):
        return f"{self.name} sagt {self.sound()}"

class Dog(Animal):
    def sound(self):
        return "Wuff"

class Cat(Animal):
    def sound(self):
        return "Miau"

class Dog(Animal) heißt: Dog erbt von Animal.

class Animal {
  constructor(name) {
    this.name = name;
  }

  sound() {
    return "...";
  }

  describe() {
    return `${this.name} sagt ${this.sound()}`;
  }
}

class Dog extends Animal {
  sound() {
    return "Wuff";
  }
}

class Cat extends Animal {
  sound() {
    return "Miau";
  }
}

class Dog extends Animal heißt: Dog erbt von Animal.

Vermutung 6Wer sagt was?Übung

Was gibt puts Dog.new("Bello").describe aus?

Wer sagt was?
Vermutung 6Wer sagt was?Übung

Was gibt print(Dog("Bello").describe()) aus?

Wer sagt was?
Vermutung 6Wer sagt was?Übung

Was gibt console.log(new Dog("Bello").describe()) aus?

Wer sagt was?

Hier geht es weiter, sobald du deine Antwort oben abgegeben hast.

describe steht nur in Animal, und trotzdem kommt Bello sagt Wuff heraus: Ruft describe die Methode sound auf, nimmt die Sprache die des Objekts, und das ist ein Dog. Dieselbe Zeile Code tut also je nach Objekt etwas anderes. Das heißt Polymorphie: Wer describe aufruft, muss nicht wissen, ob er einen Hund oder eine Katze vor sich hat.

Aufgabe 7Flächen zusammenzählenKnobelei

Schreib zwei Klassen, Square (gebaut mit einer Seitenlänge) und Rectangle (mit Breite und Höhe), beide mit einer Methode area. Dann eine Funktion total_area(shapes), die die Flächen aller Formen einer Liste zusammenzählt. total_area fragt jede Form nur nach area, ohne nachzusehen, von welcher Klasse sie ist: Die Prüfung gibt ihr auch eine Form, die du nicht kennst.

Tipp

Beide Klassen merken sich ihre Maße in initialize und haben eine Methode area, jede mit ihrer eigenen Rechnung.

Nächster Tipp

total_area geht durch die Liste und zählt shape.area zusammen, mit einer Schleife oder shapes.sum { |shape| shape.area }.

Aufgabe 7Flächen zusammenzählenKnobelei

Schreib zwei Klassen, Square (gebaut mit einer Seitenlänge) und Rectangle (mit Breite und Höhe), beide mit einer Methode area(). Dann eine Funktion total_area(shapes), die die Flächen aller Formen einer Liste zusammenzählt. total_area fragt jede Form nur nach area(), ohne nachzusehen, von welcher Klasse sie ist: Die Prüfung gibt ihr auch eine Form, die du nicht kennst.

Tipp

Beide Klassen merken sich ihre Maße in __init__ und haben eine Methode area, jede mit ihrer eigenen Rechnung.

Nächster Tipp

total_area geht durch die Liste und zählt shape.area() zusammen, mit einer Schleife oder sum(shape.area() for shape in shapes).

Aufgabe 7Flächen zusammenzählenKnobelei

Schreib zwei Klassen, Square (gebaut mit einer Seitenlänge) und Rectangle (mit Breite und Höhe), beide mit einer Methode area(). Dann eine Funktion totalArea(shapes), die die Flächen aller Formen einer Liste zusammenzählt. totalArea fragt jede Form nur nach area(), ohne nachzusehen, von welcher Klasse sie ist: Die Prüfung gibt ihr auch eine Form, die du nicht kennst.

Tipp

Beide Klassen merken sich ihre Maße im constructor und haben eine Methode area(), jede mit ihrer eigenen Rechnung.

Nächster Tipp

totalArea geht durch die Liste und zählt shape.area() zusammen, mit einer Schleife oder mit reduce.

Soll eine überschriebene Methode die der Oberklasse nicht ersetzen, sondern ergänzen, ruft sie sie mit super auf:

class Puppy < Dog
  def sound
    super + " (leise)"
  end
end

puts Puppy.new("Fips").describe    # Fips sagt Wuff (leise)

In initialize gibt super(name) den Namen an das initialize der Oberklasse weiter.

class Puppy(Dog):
    def sound(self):
        return super().sound() + " (leise)"

print(Puppy("Fips").describe())    # Fips sagt Wuff (leise)

In __init__ gibt super().__init__(name) den Namen an das __init__ der Oberklasse weiter.

class Puppy extends Dog {
  sound() {
    return super.sound() + " (leise)";
  }
}

console.log(new Puppy("Fips").describe());    // Fips sagt Wuff (leise)

Im constructor gibt super(name) den Namen an den constructor der Oberklasse weiter; erst danach darf man this benutzen.

Aufgabe 8Eine Managerin verdient mehrKnobelei

Die Klasse Employee steht schon da: Eine Angestellte hat einen Namen und ein Gehalt im Monat, und yearly gibt das Gehalt im Jahr zurück. Schreib eine Klasse Manager, die von Employee erbt, dazu einen Bonus im Jahr bekommt, Manager.new("Bo", 4000, 5000), und deren yearly den Bonus dazurechnet. Benutze dabei super, in initialize und in yearly, statt das Gehalt noch einmal selbst zu rechnen.

Tipp

Manager braucht nur den Bonus selbst. Name und Monatsgehalt gibt sein initialize mit super(name, monthly) an Employee weiter.

Nächster Tipp

In yearly ruft super das yearly von Employee auf. Dazu kommt der Bonus.

Aufgabe 8Eine Managerin verdient mehrKnobelei

Die Klasse Employee steht schon da: Eine Angestellte hat einen Namen und ein Gehalt im Monat, und yearly() gibt das Gehalt im Jahr zurück. Schreib eine Klasse Manager, die von Employee erbt, dazu einen Bonus im Jahr bekommt, Manager("Bo", 4000, 5000), und deren yearly() den Bonus dazurechnet. Benutze dabei super(), in __init__ und in yearly, statt das Gehalt noch einmal selbst zu rechnen.

Tipp

Manager braucht nur den Bonus selbst. Name und Monatsgehalt gibt sein __init__ mit super().__init__(name, monthly) an Employee weiter.

Nächster Tipp

In yearly ruft super().yearly() das yearly von Employee auf. Dazu kommt der Bonus.

Aufgabe 8Eine Managerin verdient mehrKnobelei

Die Klasse Employee steht schon da: Eine Angestellte hat einen Namen und ein Gehalt im Monat, und yearly() gibt das Gehalt im Jahr zurück. Schreib eine Klasse Manager, die von Employee erbt, dazu einen Bonus im Jahr bekommt, new Manager("Bo", 4000, 5000), und deren yearly() den Bonus dazurechnet. Benutze dabei super, im constructor und in yearly, statt das Gehalt noch einmal selbst zu rechnen.

Tipp

Manager braucht nur den Bonus selbst. Name und Monatsgehalt gibt sein constructor mit super(name, monthly) an Employee weiter; erst danach darf er this benutzen.

Nächster Tipp

In yearly ruft super.yearly() das yearly von Employee auf. Dazu kommt der Bonus.

Klassen, Objekte und Vererbung gibt es seit 1967, aus einer Sprache für Simulationen.

Kristen Nygaard (1926–2002) und Ole-Johan Dahl (1931–2002), norwegische Informatiker am Norwegischen Rechenzentrum in Oslo. In den 1960er-Jahren bauten sie Simula, eine Sprache, um Abläufe zu simulieren. In Simula 67 gab es zum ersten Mal Objekte, Klassen, Unterklassen und Vererbung.

Smalltalk übernahm die Idee, später C++ und Java. Für Simula bekamen beide den Turing Award für 2001. Jede Klasse, die du schreibst, geht auf ihre Arbeit zurück.

6. Zum Nachschlagen

7. Extras Extra

Ein Extra für Schnelle: Die Seite ist ohne dieses Kapitel vollständig.

Zwei Katas, die nicht mitzählen: ein Stack als Klasse, und ein Bruch, der rechnen kann.

Aufgabe 9Ein Stack als KlasseKnobeleiExtra

Schreib eine Klasse Stack für einen Stapel, bei dem nur das oberste Element zugänglich ist: push(x) legt x oben drauf, pop nimmt das oberste weg und gibt es zurück, peek gibt es zurück, ohne es wegzunehmen, size sagt, wie viele Elemente drauf liegen. Ist der Stack leer, geben pop und peek nil zurück.

Tipp

Der Stack merkt sich seine Elemente in einer Liste, @items = []. Oben ist ihr Ende.

Nächster Tipp

@items.push(x) legt drauf, @items.pop nimmt das letzte weg und gibt es zurück, bei einer leeren Liste nil; @items.last schaut nur.

Aufgabe 9Ein Stack als KlasseKnobeleiExtra

Schreib eine Klasse Stack für einen Stapel, bei dem nur das oberste Element zugänglich ist: push(x) legt x oben drauf, pop() nimmt das oberste weg und gibt es zurück, peek() gibt es zurück, ohne es wegzunehmen, size() sagt, wie viele Elemente drauf liegen. Ist der Stack leer, geben pop() und peek() None zurück.

Tipp

Der Stack merkt sich seine Elemente in einer Liste, self.items = []. Oben ist ihr Ende.

Nächster Tipp

self.items.append(x) legt drauf, self.items.pop() nimmt das letzte weg; bei einer leeren Liste bricht es mit einem Fehler ab, darum fragst du vorher if not self.items:. self.items[-1] schaut nur.

Aufgabe 9Ein Stack als KlasseKnobeleiExtra

Schreib eine Klasse Stack für einen Stapel, bei dem nur das oberste Element zugänglich ist: push(x) legt x oben drauf, pop() nimmt das oberste weg und gibt es zurück, peek() gibt es zurück, ohne es wegzunehmen, size() sagt, wie viele Elemente drauf liegen. Ist der Stack leer, geben pop() und peek() null zurück.

Tipp

Der Stack merkt sich seine Elemente in einer Liste, this.items = [];. Oben ist ihr Ende.

Nächster Tipp

this.items.push(x) legt drauf, this.items.pop() nimmt das letzte weg; bei einer leeren Liste gibt es undefined, darum fragst du vorher if (this.items.length === 0). this.items[this.items.length - 1] schaut nur.

Aufgabe 10Ein Bruch, der rechnen kannKnobeleiExtra

Schreib eine Klasse Fraction für Brüche, gebaut mit Zähler und Nenner, Fraction.new(2, 4), die sich gleich gekürzt merkt, also als 1/2. to_s zeigt sie als 1/2, und add(other) gibt einen neuen, gekürzten Bruch mit der Summe zurück, ohne die beiden zu ändern. Integer#gcd gibt den größten gemeinsamen Teiler: 12.gcd(18) ergibt 6.

Tipp

Für die Summe von a/b und c/d gilt: Zähler a·d + c·b, Nenner b·d. Danach kürzen.

Nächster Tipp

Kürzt initialize schon, muss add nur Fraction.new(…, …) mit dem ungekürzten Ergebnis bauen; dafür braucht add Zähler und Nenner des anderen Bruchs, also zwei Methoden, die sie zurückgeben (attr_reader).

Aufgabe 10Ein Bruch, der rechnen kannKnobeleiExtra

Schreib eine Klasse Fraction für Brüche, gebaut mit Zähler und Nenner, Fraction(2, 4), die sich gleich gekürzt merkt, also als 1/2. str() zeigt sie als 1/2, und add(other) gibt einen neuen, gekürzten Bruch mit der Summe zurück, ohne die beiden zu ändern. math.gcd(12, 18) gibt den größten gemeinsamen Teiler, 6.

Tipp

Für die Summe von a/b und c/d gilt: Zähler a·d + c·b, Nenner b·d. Danach kürzen.

Nächster Tipp

Kürzt __init__ schon, muss add nur Fraction(…, …) mit dem ungekürzten Ergebnis bauen. Die Attribute des anderen Bruchs erreichst du mit other.numerator.

Aufgabe 10Ein Bruch, der rechnen kannKnobeleiExtra

Schreib eine Klasse Fraction für Brüche, gebaut mit Zähler und Nenner, new Fraction(2, 4), die sich gleich gekürzt merkt, also als 1/2. String() zeigt sie als 1/2, und add(other) gibt einen neuen, gekürzten Bruch mit der Summe zurück, ohne die beiden zu ändern. Den größten gemeinsamen Teiler gibt die Funktion gcd, die schon dasteht.

Tipp

Für die Summe von a/b und c/d gilt: Zähler a·d + c·b, Nenner b·d. Danach kürzen.

Nächster Tipp

Kürzt der constructor schon, muss add nur new Fraction(…, …) mit dem ungekürzten Ergebnis bauen. Die Attribute des anderen Bruchs erreichst du mit other.numerator.