web-dev-qa-db-ja.com

OptionSetTypeと列挙型

ProgrammingLanguageという名前の列挙型があります。

_enum ProgrammingLanguage {
  case Swift, Haskell, Scala
}
_

これで、次のプロパティを持つProgrammerという名前のクラスができました。

_let favouriteLanguages: ProgrammingLanguage = .Swift
_

プログラマーがいくつかの好きな言語をどのように持つことができるかを見て、私は次のようなものを書くのがいいだろうと思いました:

_let favouriteLanguages: ProgrammingLanguage = [.Swift, .Haskell]
_

少し調べてみたところ、OptionSetTypeに準拠する必要があることに気付きましたが、そうすることで、次の3つのエラーが発生しました。

ProgrammingLanguageは準拠していません

  1. SetAlgebraType
  2. OptionSetType
  3. RawRepresentable

Raw Representableエラーを見たとき、私はすぐに列挙型に関連する型を考えました。とにかく列挙値を出力できるようにしたかったので、列挙署名を次のように変更しました。

_case ProgrammingLanguage: String, OptionSetType {
  case Swift, Haskell, Scala
}
_

これにより、2つの警告が沈黙しました。しかし、プロトコルSetAlgebraTypeに準拠していないという問題がまだ残っています。

少し試行錯誤した後、列挙型の関連付けられたタイプをIntで修正したことがわかりました(これは、RawRepresentableプロトコルでは署名の初期化子を実装する必要があるため、理にかなっています) init(rawValue: Int))。しかし、私はそれに満足していません。列挙型の文字列表現を簡単に取得できるようにしたいです。

誰かが私にこれを簡単に行う方法と、なぜOptionSetTypeInt関連型が必要なのか教えてもらえますか?

編集:

次の宣言は正しくコンパイルされますが、実行時にエラーが発生します。

_enum ProgrammingLanguage: Int, OptionSetType {
  case Swift, Scala, Haskell
}

extension ProgrammingLanguage {
  init(rawValue: Int) {
    self.init(rawValue: rawValue)
  }
}

let programmingLanguages: ProgrammingLanguage = [.Swift, .Scala]
_
14
Kelvin Lau

編集:私は以前の自分がこれを前もって言っていないことに驚いていますが...他の値型をOptionSetプロトコルに強制しようとする代わりに(Swift3はTypeを削除しました名前から)、これらの型を使用するAPIを検討し、必要に応じてSetコレクションを使用することをお勧めします。

OptionSetタイプは奇妙です。これらは両方ともコレクションであり、コレクションではありません。複数のフラグから1つを作成できますが、結果は単一の値のままです。 (そのような値に相当する単一フラグのコレクションを把握するためにいくつかの作業を行うことができますが、タイプで可能な値によっては、一意でない場合があります。)

一方、1つの何か、または複数の一意の何かを持つことができることは、 API。ユーザーにお気に入りが複数あると言ってもらいたいですか、それとも1つだけであることを強制しますか?いくつの「お気に入り」を許可しますか?ユーザーが複数のお気に入りを主張する場合、それらはユーザー固有の順序でランク付けする必要がありますか?これらはすべて、OptionSetスタイルのタイプでは答えるのが難しい質問ですが、Setタイプまたは他の実際のコレクションを使用するとはるかに簡単になります。

この回答の残りの部分はa)古いもので、Swift 2つの名前を使用し、b)とにかくOptionSetを実装しようとしていると想定しています。これは、あなたのAPI ...


OptionSetTypeのドキュメント を参照してください:

SetAlgebraTypeRawValueであるすべてのタイプについて、BitwiseOperationsTypeへの便利な適合性を提供します。

つまり、OptionSetTypeを採用するすべてのタイプに対してRawRepresentable適合を宣言できます。ただし、関連付けられた生の値の型がArrayLiteralConvertibleに準拠している場合に限り、(演算子とBitwiseOperationsType準拠を介して)魔法の集合代数構文のサポートを取得します。

したがって、生の値型がStringの場合、運が悪いことになります。Stringはビット単位の演算をサポートしていないため、設定された代数を取得できません。 (ここでの「楽しい」ことは、それを呼び出すことができる場合、Stringを拡張してBitwiseOperationsTypeをサポートでき、実装が axioms を満たす場合です。オプションセットの生の値として文字列を使用できます。)

無限再帰を作成したため、実行時に2番目の構文エラーが発生します。self.init(rawValue:)からinit(rawValue:)を呼び出すと、スタックが破壊されるまでゴングが保持されます。

コンパイル時のエラーなしでそれを試すことさえできるのは間違いなくバグです( ファイルしてください )。列挙型OptionSetType適合を宣言できません。理由は次のとおりです。

  1. 列挙型のセマンティックコントラクトは、それが閉集合であるということです。 ProgrammingLanguage列挙型を宣言すると、タイプProgrammingLanguageの値はSwiftScala、またはHaskellのいずれかでなければならないということになります。 、そして他には何もありません。 「SwiftandScala」の値は、そのセットには含まれていません。

  2. OptionSetTypeの基本的な実装は、整数ビットフィールドに基づいています。 「SwiftandHaskell」の値([.Swift, .Haskell])は、実際には.Swift.rawValue | .Haskell.rawValueです。これは、生の値のセットがビット整列されていない場合に問題を引き起こします。つまり、.Swift.rawValue == 1 == 0b01および.Haskell.rawValue == 2 == 0b10の場合、ビット単位またはそれらのビット単位は0b11 == 3であり、これは.Scala.rawValueと同じです。

TLDR: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)
17
rickster

現代的な方法で簡単に達成できると思います{^ _ ^}。

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]

参照: https://nshipster.com/optionset/

1
eMdOS