使用符号作为轻量级枚举
尽管 @enum
宏对于大多数用例非常有用,但在某些用例中它可能过多。@enum
的缺点包括:
- 它创建了一种新类型
- 扩展有点困难
- 它具有转换,枚举和比较等功能,在某些应用程序中可能是多余的
在需要较轻重量的替代物的情况下,可以使用 Symbol
类型。符号是实习字符串 ; 它们代表字符序列,就像字符串一样,但它们与数字唯一相关。这种独特的关联可实现快速符号相等比较
我们可能会再次实现 Card
类型,这次使用 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
我们实现内部构造函数来检查传递给构造函数的任何不正确的值。与使用 @enum
类型的示例不同,Symbol
s 可以包含任何字符串,因此我们必须小心我们接受哪种类型的 Symbol
s。这里注意使用短路条件运算符。
现在我们可以像我们期望的那样构建 Card
对象:
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
Symbol
s 的一个主要好处是它们的运行时可扩展性。如果在运行时,我们希望接受(例如):eleven
作为新排名,只需运行 push!(ranks, :eleven)
就足够了。@enum
类型无法实现这种运行时可扩展性。