Julia Language
Enums
Szukaj…
Składnia
- @enum EnumType val = 1 val val
- :symbol
Uwagi
Czasami przydaje się wyliczenie typów, w których każda instancja jest innego typu (często niezmienny typ singletonu ); może to być ważne dla stabilności typu. Cechy są zwykle realizowane za pomocą tego paradygmatu. Powoduje to jednak dodatkowe obciążenie związane z czasem kompilacji.
Definiowanie wyliczonego typu
Typ wyliczony to typ, który może przechowywać jedną ze skończonych list możliwych wartości. W Julii typy wyliczane są zwykle nazywane „typami wyliczeniowymi”. Na przykład można użyć typów wyliczeniowych do opisania siedmiu dni tygodnia, dwunastu miesięcy roku, czterech kolorów standardowej talii 52 kart lub innych podobnych sytuacji.
Możemy zdefiniować wyliczone typy do modelowania kolorów i rang standardowych talii 52 kart. Makro @enum
służy do definiowania typów @enum
.
@enum Suit ♣ ♦ ♥ ♠
@enum Rank ace=1 two three four five six seven eight nine ten jack queen king
Definiuje dwa typy: Suit
i Rank
. Możemy sprawdzić, czy wartości są rzeczywiście oczekiwanych typów:
julia> ♦
♦::Suit = 1
julia> six
six::Rank = 6
Pamiętaj, że każdy kolor i ranga są powiązane z liczbą. Domyślnie liczba ta zaczyna się od zera. Drugi kolor, diamenty, otrzymał numer 1. W przypadku Rank
sensowniejsze może być rozpoczęcie liczby od razu. Osiągnięto to poprzez opatrzenie definicji ace
adnotacją =1
.
Typy wyliczane mają wiele funkcji, takich jak wbudowana równość (a nawet tożsamość) i wbudowane porównania:
julia> seven === seven
true
julia> ten ≠ jack
true
julia> two < three
true
Podobnie jak wartości dowolnego innego niezmiennego typu , wartości wyliczonych typów mogą być również mieszane i przechowywane w Dict
s.
Możemy uzupełnić ten przykład, definiując typ Card
która ma pole Rank
i Suit
:
immutable Card
rank::Rank
suit::Suit
end
i stąd możemy tworzyć karty
julia> Card(three, ♣)
Card(three::Rank = 3,♣::Suit = 0)
Ale wyliczone typy mają również własne metody convert
, więc rzeczywiście możemy to po prostu zrobić
julia> Card(7, ♠)
Card(seven::Rank = 7,♠::Suit = 3)
a ponieważ 7
można bezpośrednio przekonwertować na Rank
, ten konstruktor działa od razu po wyjęciu z pudełka.
Możemy chcieć zdefiniować cukier syntaktyczny do budowy tych kart; niejawne mnożenie zapewnia wygodny sposób na zrobienie tego. Definiować
julia> import Base.*
julia> r::Int * s::Suit = Card(r, s)
* (generic function with 156 methods)
i wtedy
julia> 10♣
Card(ten::Rank = 10,♣::Suit = 0)
julia> 5♠
Card(five::Rank = 5,♠::Suit = 3)
jeszcze raz wykorzystując wbudowane funkcje convert
.
Używanie symboli jako lekkich wyliczeń
Chociaż makro @enum
jest bardzo przydatne w większości przypadków użycia, w niektórych przypadkach może być nadmierne. Wady @enum
obejmują:
- Tworzy nowy typ
- Trochę trudniej jest go rozszerzyć
- Zawiera funkcje takie jak konwersja, wyliczanie i porównywanie, które mogą być zbędne w niektórych aplikacjach
W przypadkach, gdy pożądana jest lekka alternatywa, można użyć typu Symbol
. Symbole są ciągami wewnętrznymi ; reprezentują sekwencje znaków, podobnie jak łańcuchy , ale są jednoznacznie powiązane z liczbami. To unikalne skojarzenie umożliwia szybkie porównanie równości symboli.
Możemy ponownie zaimplementować typ Card
, tym razem używając pól 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
Implementujemy wewnętrzny konstruktor, aby sprawdzić, czy niepoprawne wartości są przekazywane do konstruktora. W przeciwieństwie do przykładu z użyciem typów @enum
, Symbol
s mogą zawierać dowolny ciąg znaków, dlatego musimy uważać na to, jakie rodzaje Symbol
akceptujemy. Zwróć uwagę na użycie zwarciowych operatorów warunkowych.
Teraz możemy konstruować obiekty Card
tak, jak się spodziewamy:
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
Główną zaletą Symbol
jest ich rozszerzalność. Jeśli w czasie wykonywania chcemy zaakceptować (na przykład) :eleven
jako nową rangę, wystarczy uruchomić push!(ranks, :eleven)
. Taka rozszerzalność środowiska wykonawczego nie jest możliwa w @enum
typów @enum
.