Julia Language
Enums
Ricerca…
Sintassi
- @enum EnumType val = 1 val val
- :simbolo
Osservazioni
A volte è utile avere tipi enumerati in cui ogni istanza è di un tipo diverso (spesso un tipo immutabile singleton ); questo può essere importante per la stabilità del tipo. I tratti sono tipicamente implementati con questo paradigma. Tuttavia, ciò comporta un ulteriore sovraccarico in fase di compilazione.
Definizione di un tipo enumerato
Un tipo enumerato è un tipo che può contenere uno di un elenco finito di valori possibili. In Julia, i tipi enumerati sono in genere chiamati "tipi enum". Ad esempio, uno potrebbe usare i tipi di enum per descrivere i sette giorni della settimana, i dodici mesi dell'anno, i quattro semi di un mazzo di 52 carte standard o altre situazioni simili.
Possiamo definire tipi enumerati per modellare i semi e le truppe di un mazzo di 52 carte standard. La macro @enum
viene utilizzata per definire i tipi di enum.
@enum Suit ♣ ♦ ♥ ♠
@enum Rank ace=1 two three four five six seven eight nine ten jack queen king
Questo definisce due tipi: Suit
e Rank
. Possiamo verificare che i valori siano effettivamente dei tipi previsti:
julia> ♦
♦::Suit = 1
julia> six
six::Rank = 6
Si noti che ogni seme e classifica sono stati associati a un numero. Per impostazione predefinita, questo numero inizia da zero. Quindi il secondo seme, quadri, è stato assegnato al numero 1. Nel caso di Rank
, potrebbe essere più sensato iniziare il numero a uno. Ciò è stato ottenuto annotando la definizione di ace
con annotazione a =1
.
I tipi enumerati sono dotati di molte funzionalità, come l'uguaglianza (e in effetti l'identità) e i confronti integrati:
julia> seven === seven
true
julia> ten ≠ jack
true
julia> two < three
true
Come i valori di qualsiasi altro tipo immutabile , anche i valori dei tipi enumerati possono essere sottoposti a hash e memorizzati in Dict
s.
Possiamo completare questo esempio definendo un tipo di Card
che ha un campo Rank
e un campo Suit
:
immutable Card
rank::Rank
suit::Suit
end
e quindi possiamo creare carte con
julia> Card(three, ♣)
Card(three::Rank = 3,♣::Suit = 0)
Ma i tipi elencati includono anche i propri metodi di convert
, quindi possiamo semplicemente farlo
julia> Card(7, ♠)
Card(seven::Rank = 7,♠::Suit = 3)
e poiché 7
può essere convertito direttamente in Rank
, questo costruttore funziona fuori dalla scatola.
Potremmo voler definire lo zucchero sintattico per costruire queste carte; la moltiplicazione implicita fornisce un modo conveniente per farlo. Definire
julia> import Base.*
julia> r::Int * s::Suit = Card(r, s)
* (generic function with 156 methods)
e poi
julia> 10♣
Card(ten::Rank = 10,♣::Suit = 0)
julia> 5♠
Card(five::Rank = 5,♠::Suit = 3)
ancora una volta sfruttando le funzioni di convert
integrate.
Usare simboli come leggere enumerazioni
Sebbene la macro @enum
sia abbastanza utile per la maggior parte dei casi d'uso, può essere eccessiva in alcuni casi d'uso. Gli svantaggi di @enum
includono:
- Crea un nuovo tipo
- È un po 'più difficile da estendere
- Viene fornito con funzionalità come la conversione, l'enumerazione e il confronto, che possono essere superflui in alcune applicazioni
Nei casi in cui si desideri un'alternativa più leggera, è possibile utilizzare il tipo di Symbol
. I simboli sono stringhe internate ; rappresentano sequenze di personaggi, proprio come fanno gli archi , ma sono associati in modo univoco con i numeri. Questa associazione unica consente il confronto veloce dell'uguaglianza dei simboli.
Potremmo implementare nuovamente un tipo di Card
, questa volta utilizzando i campi Symbol
:
const ranks = Set([:ace, :two, :three, :four, :five, :six, :seven, :eight, :nine,
:ten, :jack, :queen, :king])
const suits = Set([:♣, :♦, :♥, :♠])
immutable Card
rank::Symbol
suit::Symbol
function Card(r::Symbol, s::Symbol)
r in ranks || throw(ArgumentError("invalid rank: $r"))
s in suits || throw(ArgumentError("invalid suit: $s"))
new(r, s)
end
end
Implementiamo il costruttore interno per verificare eventuali valori errati passati al costruttore. Diversamente dall'esempio che usa i tipi @enum
, Symbol
s può contenere qualsiasi stringa, quindi dobbiamo stare attenti a quali tipi di Symbol
accettiamo. Nota qui l'uso degli operatori condizionali di cortocircuito .
Ora possiamo costruire oggetti Card
come ci aspettiamo:
julia> Card(:ace, :♦)
Card(:ace,:♦)
julia> Card(:nine, :♠)
Card(:nine,:♠)
julia> Card(:eleven, :♠)
ERROR: ArgumentError: invalid rank: eleven
in Card(::Symbol, ::Symbol) at ./REPL[17]:5
julia> Card(:king, :X)
ERROR: ArgumentError: invalid suit: X
in Card(::Symbol, ::Symbol) at ./REPL[17]:6
Un importante vantaggio di Symbol
s è la loro estensibilità al runtime. Se in fase di esecuzione, desideriamo accettare (per esempio) :eleven
come nuovo rango, è sufficiente eseguire semplicemente push!(ranks, :eleven)
Rank,: push!(ranks, :eleven)
. Tale estensibilità del runtime non è possibile con i tipi @enum
.