ProgrammingLanguage
という名前の列挙型があります。
_enum ProgrammingLanguage {
case Swift, Haskell, Scala
}
_
これで、次のプロパティを持つProgrammer
という名前のクラスができました。
_let favouriteLanguages: ProgrammingLanguage = .Swift
_
プログラマーがいくつかの好きな言語をどのように持つことができるかを見て、私は次のようなものを書くのがいいだろうと思いました:
_let favouriteLanguages: ProgrammingLanguage = [.Swift, .Haskell]
_
少し調べてみたところ、OptionSetType
に準拠する必要があることに気付きましたが、そうすることで、次の3つのエラーが発生しました。
ProgrammingLanguageは準拠していません
SetAlgebraType
OptionSetType
RawRepresentable
Raw Representableエラーを見たとき、私はすぐに列挙型に関連する型を考えました。とにかく列挙値を出力できるようにしたかったので、列挙署名を次のように変更しました。
_case ProgrammingLanguage: String, OptionSetType {
case Swift, Haskell, Scala
}
_
これにより、2つの警告が沈黙しました。しかし、プロトコルSetAlgebraType
に準拠していないという問題がまだ残っています。
少し試行錯誤した後、列挙型の関連付けられたタイプをInt
で修正したことがわかりました(これは、RawRepresentable
プロトコルでは署名の初期化子を実装する必要があるため、理にかなっています) init(rawValue: Int)
)。しかし、私はそれに満足していません。列挙型の文字列表現を簡単に取得できるようにしたいです。
誰かが私にこれを簡単に行う方法と、なぜOptionSetType
にInt
関連型が必要なのか教えてもらえますか?
編集:
次の宣言は正しくコンパイルされますが、実行時にエラーが発生します。
_enum ProgrammingLanguage: Int, OptionSetType {
case Swift, Scala, Haskell
}
extension ProgrammingLanguage {
init(rawValue: Int) {
self.init(rawValue: rawValue)
}
}
let programmingLanguages: ProgrammingLanguage = [.Swift, .Scala]
_
編集:私は以前の自分がこれを前もって言っていないことに驚いていますが...他の値型をOptionSet
プロトコルに強制しようとする代わりに(Swift3はType
を削除しました名前から)、これらの型を使用するAPIを検討し、必要に応じてSet
コレクションを使用することをお勧めします。
OptionSet
タイプは奇妙です。これらは両方ともコレクションであり、コレクションではありません。複数のフラグから1つを作成できますが、結果は単一の値のままです。 (そのような値に相当する単一フラグのコレクションを把握するためにいくつかの作業を行うことができますが、タイプで可能な値によっては、一意でない場合があります。)
一方、1つの何か、または複数の一意の何かを持つことができることは、 API。ユーザーにお気に入りが複数あると言ってもらいたいですか、それとも1つだけであることを強制しますか?いくつの「お気に入り」を許可しますか?ユーザーが複数のお気に入りを主張する場合、それらはユーザー固有の順序でランク付けする必要がありますか?これらはすべて、OptionSet
スタイルのタイプでは答えるのが難しい質問ですが、Set
タイプまたは他の実際のコレクションを使用するとはるかに簡単になります。
この回答の残りの部分はa)古いもので、Swift 2つの名前を使用し、b)とにかくOptionSet
を実装しようとしていると想定しています。これは、あなたのAPI ...
OptionSetType
のドキュメント を参照してください:
SetAlgebraType
がRawValue
であるすべてのタイプについて、BitwiseOperationsType
への便利な適合性を提供します。
つまり、OptionSetType
を採用するすべてのタイプに対してRawRepresentable
適合を宣言できます。ただし、関連付けられた生の値の型がArrayLiteralConvertible
に準拠している場合に限り、(演算子とBitwiseOperationsType
準拠を介して)魔法の集合代数構文のサポートを取得します。
したがって、生の値型がString
の場合、運が悪いことになります。String
はビット単位の演算をサポートしていないため、設定された代数を取得できません。 (ここでの「楽しい」ことは、それを呼び出すことができる場合、String
を拡張してBitwiseOperationsType
をサポートでき、実装が axioms を満たす場合です。オプションセットの生の値として文字列を使用できます。)
無限再帰を作成したため、実行時に2番目の構文エラーが発生します。self.init(rawValue:)
からinit(rawValue:)
を呼び出すと、スタックが破壊されるまでゴングが保持されます。
コンパイル時のエラーなしでそれを試すことさえできるのは間違いなくバグです( ファイルしてください )。列挙型はOptionSetType
適合を宣言できません。理由は次のとおりです。
列挙型のセマンティックコントラクトは、それが閉集合であるということです。 ProgrammingLanguage
列挙型を宣言すると、タイプProgrammingLanguage
の値はSwift
、Scala
、またはHaskell
のいずれかでなければならないということになります。 、そして他には何もありません。 「SwiftandScala」の値は、そのセットには含まれていません。
OptionSetType
の基本的な実装は、整数ビットフィールドに基づいています。 「SwiftandHaskell」の値([.Swift, .Haskell]
)は、実際には.Swift.rawValue | .Haskell.rawValue
です。これは、生の値のセットがビット整列されていない場合に問題を引き起こします。つまり、.Swift.rawValue == 1 == 0b01
および.Haskell.rawValue == 2 == 0b10
の場合、ビット単位またはそれらのビット単位は0b11 == 3
であり、これは.Scala.rawValue
と同じです。
OptionSetType
適合が必要な場合は、構造体を宣言します。そして、static let
を使用して、タイプのメンバーを宣言します。
そして、他のメンバーの可能な(ビット単位または)組み合わせと区別したいメンバーが実際にそうであるように、生の値を選択します。
struct ProgrammingLanguage: OptionSetType {
let rawValue: Int
// this initializer is required, but it's also automatically
// synthesized if `rawValue` is the only member, so writing it
// here is optional:
init(rawValue: Int) { self.rawValue = rawValue }
static let Swift = ProgrammingLanguage(rawValue: 0b001)
static let Haskell = ProgrammingLanguage(rawValue: 0b010)
static let Scala = ProgrammingLanguage(rawValue: 0b100)
}
値を区別するための良い方法:上記のようにバイナリリテラル構文を使用するか、以下のように1のビットシフトで値を宣言します。
static let Swift = ProgrammingLanguage(rawValue: 1 << 0)
static let Haskell = ProgrammingLanguage(rawValue: 1 << 1)
static let Scala = ProgrammingLanguage(rawValue: 1 << 2)
現代的な方法で簡単に達成できると思います{^ _ ^}。
protocol Option: RawRepresentable, Hashable, CaseIterable {}
extension Set where Element: Option {
var rawValue: Int {
var rawValue = 0
for (index, element) in Element.allCases.enumerated() where contains(element) {
rawValue |= (1 << index)
}
return rawValue
}
}
...そして
enum ProgrammingLanguage: String, Option {
case Swift, Haskell, Scala
}
typealias ProgrammingLanguages = Set<ProgrammingLanguage>
let programmingLanguages: ProgrammingLanguages = [.Swift, .Haskell]