Track
/
Crystal
Crystal
/
Esercizi
/
Acciaio incantato
Acciaio incantato

Acciaio incantato

Esercizio di apprendimento

Introduzione

In programmazione, l'iterazione si riferisce al processo di ripetere un blocco di codice più volte. Questo può essere fatto usando concetti come i cicli while e i cicli until. Può anche essere fatto usando la ricorsione, cioè una funzione che chiama se stessa.

Tuttavia, il più delle volte vuoi iterare su una collezione di elementi, come un Array, un Range o una String. Sia la classe Array che la classe Range in Crystal includono il modulo Enumerable, che fornisce una serie di metodi per iterare sugli elementi. La classe String, invece, ha il suo insieme di metodi per iterare sui caratteri.

Inoltre Crystal non ha alcuna istruzione for come altri linguaggi. Al suo posto ha diversi metodi che si possono usare per iterare.

Iterare

Il modo più comune per iterare su una collezione è usare il metodo each, che passa a un blocco ogni elemento della collezione. Questo si può fare facilmente con un Range. Supponi di voler iterare da 1 a 3: puoi usare il metodo each per iterare sull'intervallo.

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

# Output:
# 1
# 2
# 3

Ancora più semplice: se vuoi solo iterare un certo numero di volte, puoi usare il metodo times, che esiste sulla classe Int.

3.times do |n|
  puts n
end

# Output:
# 0
# 1
# 2

Iterare su una String

Una String è una sequenza di caratteri e non appartiene al modulo Enumerable, il che significa che ha il suo insieme di metodi per iterare sui caratteri. Il modo più comune per iterare su una String è usare il metodo each_char, che passa a un blocco ogni carattere della String.

Note

Il metodo each_char passa al blocco un oggetto Char e non un oggetto String.

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

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

Un altro modo per iterare su una String è usare il metodo each_line. Questo metodo si usa soprattutto quando si legge un file riga per riga.

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

# Output:
# hello
# world

Iterare su un oggetto che include il modulo Enumerable

Il modulo Enumerable fornisce una serie di metodi per iterare sugli elementi di una collezione. Le collezioni che includono il modulo Enumerable sono Array, Range, Hash, Set e altre; queste ultime saranno trattate nei concetti successivi.

Il modo più comune per iterare su un Array è usare il metodo each, che passa a un blocco ogni elemento dell'Array.

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

# Output:
# 5
# 2
# 3

Map

Il metodo map è un altro modo per iterare su un Array: restituisce un nuovo Array che contiene i risultati dell'applicazione del blocco a ogni elemento. È simile al metodo each, ma restituisce un nuovo Array con gli elementi trasformati. La trasformazione è molto utile quando vuoi applicare una funzione a ogni elemento della collezione, un'operazione comune.

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

new_arr
# => [2, 4, 6]

Con un flusso di controllo come if puoi anche filtrare quali elementi trasformare.

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

new_arr
# => [2, 2, 6]

Con indice

A volte ti serve conoscere l'indice dell'elemento su cui stai iterando: per farlo puoi usare il metodo each_with_index o map_with_index. Passa a un blocco ogni elemento e il suo indice; il metodo accetta anche un argomento opzionale per specificare l'indice di partenza.

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

Nota che il metodo each_char_with_index è disponibile anche per gli oggetti String.

sum

Il metodo sum restituisce la somma di tutti gli elementi della collezione; accetta anche un blocco opzionale per trasformare gli elementi prima di sommarli. Accetta inoltre un argomento opzionale per specificare il valore iniziale della somma.

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

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

reduce

reduce, o fold come è noto in altri linguaggi, è un metodo che, con un metodo di combinazione e un valore iniziale, combina tutti gli elementi della collezione. Il metodo reduce ha un accumulatore che viene passato al blocco; l'accumulatore è il risultato dell'iterazione precedente. Si tratta di un processo ricorsivo che combina tutti gli elementi della collezione.

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

reduce può sembrare simile a sum, ma è più flessibile perché ti permette di specificare il valore iniziale dell'accumulatore e il metodo di combinazione. reduce può essere usato per implementare sum, count e altri metodi.

Istruzioni

Nel gioco di carte Spellbound Steel, hai un mazzo di carte con personaggi diversi, come warriors, mages e rogues, oppure incantesimi diversi, come fireball, ice storm e lightning bolt. In partita ci sono da due a quattro giocatori, e l'obiettivo è sconfiggere gli altri giocatori giocando carte con livelli di potenza più alti.

Vuoi avere la meglio nel gioco creando vari metodi per calcolare, ordinare e confrontare i livelli di potenza delle carte.

1. Trovare una carta nel mazzo

Non sei sicuro che una carta sia nel mazzo oppure no. Perciò vuoi un metodo che ti dica dove trovare la prima carta del mazzo del tipo che stai cercando. Oppure, se la carta non è nel mazzo, vuoi saperlo lo stesso.

Implementa un metodo SpellboundSteel.find_card? che prende un Array di carte String e una carta String da trovare. Il metodo deve restituire l'indice della prima carta del mazzo che corrisponde alla carta da cercare. Se la carta non è nel mazzo, il metodo deve restituire nil.

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

2. Mettere in maiuscolo tutti i caratteri

Per rendere le carte più facili da leggere, vuoi mettere in maiuscolo tutti i caratteri nei nomi delle carte. Così puoi vedere facilmente i nomi delle carte quando le giochi.

Implementa un metodo SpellboundSteel.capitalize_cards che prende un Array di carte String e restituisce le carte con tutti i caratteri in maiuscolo.

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

3. Calcolare il livello di potenza di tutte le carte

Nel gioco è importante calcolare il livello di potenza di tutte le carte. La potenza di tutte le carte è visibile nella tabella seguente:

Tipo di carta Livello di potenza
Warrior 10
Mage 20
Rogue 30
Fireball 15
Ice Storm 25
Lightning Bolt 35

Implementa un metodo SpellboundSteel.calculate_power_level che prende un Array di carte String e restituisce il livello di potenza totale di tutte le carte.

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

4. Decodificare una carta

Nel gioco esistono quelle che vengono chiamate carte maledette. Queste carte sono una carta nascosta che può essere decodificata solo dal giocatore che l'ha giocata. Per decodificare la carta devi prendere un carattere sì e uno no nel nome della carta e rimuoverlo. Otterrai così il nome della carta.

Implementa un metodo SpellboundSteel.decode_card che prende una carta String e restituisce la carta decodificata.

SpellboundSteel.decode_card("Wbalrrrlimoqr")
# => "Warrior"
Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
Crystal Exercism

Vuoi iniziare Acciaio incantato?

Iscriviti a Exercism per imparare e padroneggiare Crystal con 26 concetti142 esercizi e il mentoring di persone reali, tutto gratis.