Tracks
/
Crystal
Crystal
/
Übungen
/
Verzauberter Stahl
Verzauberter Stahl

Verzauberter Stahl

Lernübung

Einführung

In der Programmierung bezeichnet Iteration den Vorgang, einen Block Code mehrmals zu wiederholen. Das kannst du mit Konzepten wie while-Schleifen und until-Schleifen erreichen. Ebenso geht es mit Rekursion, also einer Funktion, die sich selbst aufruft.

Meistens möchtest du aber über eine Sammlung von Elementen iterieren, etwa ein Array, ein Range oder ein String. Die Klassen Array und Range in Crystal binden beide das Modul Enumerable ein, das eine Reihe von Methoden zum Iterieren über die Elemente bereitstellt. Die Klasse String hat dagegen ihre eigenen Methoden, um über die Zeichen zu iterieren.

Außerdem gibt es in Crystal keine for-Anweisung wie in anderen Sprachen. Stattdessen gibt es mehrere Methoden, mit denen du iterieren kannst.

Iterieren

Am häufigsten iterierst du über eine Sammlung mit der Methode each, die jedes Element der Sammlung an einen Block übergibt. Mit einem Range geht das ganz einfach. Angenommen, du möchtest von 1 bis 3 schleifen, dann kannst du die Methode each verwenden, um über den Range zu iterieren.

(1..3).each do |n|
  puts n
end

# Output:
# 1
# 2
# 3

Noch einfacher: Wenn du nur eine bestimmte Anzahl von Malen iterieren möchtest, kannst du die Methode times verwenden, die es in der Klasse Int gibt.

3.times do |n|
  puts n
end

# Output:
# 0
# 1
# 2

Über einen String iterieren

Ein String ist eine Folge von Zeichen und gehört nicht zum Modul Enumerable. Deshalb hat er seine eigenen Methoden, um über die Zeichen zu iterieren. Am häufigsten iterierst du über einen String mit der Methode each_char, die jedes Zeichen des String an einen Block übergibt.

Note

Die Methode each_char übergibt dem Block ein Char-Objekt und kein String-Objekt.

str = "hello"
str.each_char { |char| puts char }

# Output:
# h
# e
# l
# l
# o

Eine andere Möglichkeit, über einen String zu iterieren, ist die Methode each_line. Diese Methode wird vor allem verwendet, wenn du eine Datei Zeile für Zeile liest.

str = "hello\nworld"
str.each_line do |line|
  puts line
end

# Output:
# hello
# world

Über ein Objekt iterieren, das das Modul Enumerable einbindet

Das Modul Enumerable stellt eine Reihe von Methoden bereit, um über die Elemente einer Sammlung zu iterieren. Sammlungen, die das Modul Enumerable einbinden, sind Array, Range, Hash, Set und andere. Die letzteren werden in späteren Konzepten behandelt.

Am häufigsten iterierst du über ein Array mit der Methode each, die jedes Element des Array an einen Block übergibt.

arr = [5, 2, 3]
arr.each do |element|
  puts element
end

# Output:
# 5
# 2
# 3

Map

Die Methode map ist eine weitere Möglichkeit, über ein Array zu iterieren. Sie gibt ein neues Array zurück, das die Ergebnisse enthält, wenn du den Block auf jedes Element anwendest. Sie ähnelt der Methode each, gibt aber ein neues Array mit den transformierten Elementen zurück. Transformation ist sehr nützlich, wenn du eine Funktion auf jedes Element der Sammlung anwenden möchtest, was eine häufige Operation ist.

arr = [1, 2, 3]
new_arr = arr.map do |element|
  element * 2
end

new_arr
# => [2, 4, 6]

Mit einer Kontrollstruktur wie if kannst du auch filtern, welche Elemente transformiert werden.

arr = [1, 2, 3]
new_arr = arr.map do |element|
  if element.odd?
    element * 2
  else
    element
  end
end

new_arr
# => [2, 2, 6]

Mit Index

Manchmal musst du den Index des Elements kennen, über das du gerade iterierst. Dafür kannst du die Methode each_with_index oder map_with_index verwenden. Sie übergibt jedes Element und seinen Index an einen Block. Die Methode akzeptiert außerdem ein optionales Argument, um den Startindex anzugeben.

arr = [1, 2, 3]
arr.each_with_index(4) do |element, index|
  puts "Element: #{element}, Index: #{index}"
end

# Output:
# Element: 1, Index: 4
# Element: 2, Index: 5
# Element: 3, Index: 6

Beachte, dass die Methode each_char_with_index auch für String-Objekte verfügbar ist.

sum

Die Methode sum gibt die Summe aller Elemente der Sammlung zurück. Sie akzeptiert außerdem einen optionalen Block, um die Elemente vor dem Summieren zu transformieren. Sie akzeptiert auch ein optionales Argument, um den Startwert der Summe festzulegen.

arr = [1, 2, 3]
arr.sum
# => 6

arr.sum(2) { |n| n * 2 }
# => 14

reduce

reduce, in anderen Sprachen auch als fold bekannt, ist eine Methode, die mit einer Kombinierungsmethode und einem Startwert alle Elemente der Sammlung zusammenführt. Die Methode reduce hat einen Akkumulator, der an den Block übergeben wird. Der Akkumulator ist das Ergebnis der vorherigen Iteration. So entsteht ein rekursiver Prozess, der alle Elemente der Sammlung zusammenführt.

arr = [1, 2, 3]
arr.reduce(0) do |acc, n|
  acc + n
end
# => 6

reduce mag sum ähneln, aber reduce ist flexibler, weil du den Startwert des Akkumulators und die Kombinierungsmethode angeben kannst. Mit reduce kannst du sum, count und andere Methoden implementieren.

Anleitung

Im Kartenspiel Spellbound Steel hast du ein Deck mit Karten, die entweder verschiedene Charaktere wie warrior, mage und rogue oder verschiedene Zaubersprüche wie fireball, ice storm und lightning bolt zeigen. Es spielen zwei bis vier Personen mit, und das Ziel ist, die anderen Spieler zu besiegen, indem du Karten mit höheren Kraftwerten ausspielst.

Du willst im Spiel die Oberhand gewinnen und erstellst dafür verschiedene Methoden, um die Kraftwerte der Karten zu berechnen, zu sortieren und zu vergleichen.

1. Eine Karte im Deck finden

Du bist dir nicht sicher, ob eine Karte im Deck ist oder nicht. Deshalb willst du eine Methode, die dir sagt, wo du die erste Karte der gesuchten Art im Deck findest. Und wenn die Karte nicht im Deck ist, willst du das ebenfalls wissen.

Implementiere eine Methode SpellboundSteel.find_card?, die ein Array aus String-Karten und eine zu suchende String-Karte entgegennimmt. Die Methode soll den Index der ersten Karte im Deck zurückgeben, die zur gesuchten Karte passt. Wenn die Karte nicht im Deck ist, soll die Methode nil zurückgeben.

cards = ["Warrior", "Mage", "Rogue", "Fireball", "Ice Storm", "Lightning Bolt", "Mage"]
SpellboundSteel.find_card?(cards, "Mage")
# => 1

2. Alle Zeichen großschreiben

Damit die Karten leichter zu lesen sind, willst du alle Zeichen in den Kartennamen großschreiben. So kannst du die Kartennamen gut erkennen, wenn du die Karten ausspielst.

Implementiere eine Methode SpellboundSteel.capitalize_cards, die ein Array aus String-Karten entgegennimmt und die Karten mit allen großgeschriebenen Zeichen zurückgibt.

cards = ["warrior", "mage", "rogue", "fireball", "ice storm", "lightning bolt"]
SpellboundSteel.capitalize_cards(cards)
# => ["Warrior", "Mage", "Rogue", "Fireball", "Ice Storm", "Lightning Bolt"]

3. Den Kraftwert aller Karten berechnen

Im Spiel ist es wichtig, den Kraftwert aller Karten zu berechnen. Die Kraftwerte aller Karten findest du in der folgenden Tabelle:

Card Type Power Level
Warrior 10
Mage 20
Rogue 30
Fireball 15
Ice Storm 25
Lightning Bolt 35

Implementiere eine Methode SpellboundSteel.calculate_power_level, die ein Array aus String-Karten entgegennimmt und den gesamten Kraftwert aller Karten zurückgibt.

cards = ["Warrior", "Mage", "Rogue", "Fireball", "Ice Storm", "Lightning Bolt"]
SpellboundSteel.calculate_power_level(cards)
# => 135

4. Eine Karte entschlüsseln

Im Spiel gibt es sogenannte verfluchte Karten. Diese Karten sind versteckte Karten, die nur die Person entschlüsseln kann, die sie ausgespielt hat. Um die Karte zu entschlüsseln, musst du jedes zweite Zeichen im Kartennamen nehmen und entfernen. So erhältst du den Namen der Karte.

Implementiere eine Methode SpellboundSteel.decode_card, die eine String-Karte entgegennimmt und die entschlüsselte Karte zurückgibt.

SpellboundSteel.decode_card("Wbalrrrlimoqr")
# => "Warrior"
Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
Crystal Exercism

Bereit, mit Verzauberter Stahl zu starten?

Melde dich bei Exercism an, um Crystal mit 26 Konzepte142 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.