带倒置选项的OptionSet

时间:2019-02-14 10:49:54

标签: swift

例如,我有一组选项可以传递给某些流程,例如

struct Options: OptionSet {
   let rawValue: Int

   static let floogled = Options(rawValue: 1 << 0)
   static let jibjabbed = Options(rawValue: 1 << 1)
}

这些被传递给某些功能,例如foo(options: .floogled)bar(options: .jibjabbed)

现在,通常当我调用这些函数时,意图是启用所有选项。因此,我可以使用默认值定义函数,例如

func foo(options: Options = [.floogled, .jibjabbed]) {
   ...
}

,依此类推,然后只需调用foo()。到目前为止一切顺利。

但是,在特殊情况下,我不想在启用所有选项的情况下调用这些函数,如果我可以指定要启用 not 的选项,则用法会更加直观比哪些选项保持启用状态。例如,我想打电话给foo(options: .notFloogled)而不是foo(options: .jibjabbed)

我可以想到两种方法来做到这一点。第一种方法是更改​​选项集的定义,以便否定地定义其成员。换句话说

struct OptionsAlternative: OptionSet {
    let rawValue: Int

    static let notFloogled = Options(rawValue: 1 << 0)
    static let notJibjabbed = Options(rawValue: 1 << 1)
}

然后将在默认情况下在不禁用任何选项的情况下定义功能;即

func foo(options: OptionsAlternative = []) {
   ...
}

对于默认行为,我可以像foo()那样称呼它,而当我需要禁用特定选项时,可以像foo(options: .notFloogled)这样称呼它。但是,我不想这样做,因为这会使函数的实现变得不直观。我必须以双重否定的方式实现考虑了这些选项的部分。比较这是多么笨拙:

if !options.contains(.notFloogled) {
   // Floogling is enabled. Floogle the foo.
   ...
}

相对

if options.contains(.floogled) {
   // Floogling is enabled. Floogle the foo.
   ...
}

第二种方法是包括成员以指示选项的存在,如在原始解决方案中一样,以及便利成员用于指示选项的不存在。即

extension Options {
   /// Not floogled; only jibjabbed.
   static let notFloogled: Options = [.jibjabbed]

   /// Not jibjabbed; only floogled.
   static let notJibjabbed: Options = [.floogled]
}

但是,如果我要禁用多个选项(这可能是首先使用选项集的主要原因,这是不可行的),则无法使用。例如,如果我现在叫foo(options: [.notFloogled, .notJibjabbed]),则会得到一个启用了 both 浮动和短刺的选项集,而不是 都不是。

在保持实现和用法尽可能直观的同时,还有另一种方法可以实现我想要实现的目标吗?

3 个答案:

答案 0 :(得分:3)

OptionSet是一个 set 。它有一个代数(SetAlgebra)。因此,您不需要“反向选项”; you 反转。要进行反演,只需自己从“全部”情况中减去。

要进行说明(使用Apple自己的OptionSet示例):

struct ShippingOptions: OptionSet {
    let rawValue: Int
    static let nextDay    = ShippingOptions(rawValue: 1 << 0)
    static let secondDay  = ShippingOptions(rawValue: 1 << 1)
    static let priority   = ShippingOptions(rawValue: 1 << 2)
    static let standard   = ShippingOptions(rawValue: 1 << 3)
    // include common combinations, including an all case
    static let express: ShippingOptions = [.nextDay, .secondDay]
    static let all: ShippingOptions = [.express, .priority, .standard]
}

// now form the desired options using subtractions
let negatives : ShippingOptions = [.standard] // the ones you _don't_ want
let opts = ShippingOptions.all.subtracting(negatives) // subtract them from "all"!
// and pass `opts`

答案 1 :(得分:0)

这有点令人困惑。那么,这些选项是否互相排斥?因为您的补语.notFloogled说是.jibjiabbed(在最后一个示例中)。但是在顶部,您可以同时启用这两个选项。更复杂的是,如果结构中同时包含变量.floogled.notFloogled,则可以将两个变量都添加到foo()的array参数中,然后处理哪个选项。 / p>

要解决补码问题: 我猜原始值说明了在二进制代码中启用哪个选项。 因此,我假设1表示启用,0表示不执行任何操作。 在对.notFloogled使用补码的情况下,我将重新解释二进制代码,并说1 isnotEnable和0不执行任何操作。否则,如果您说notFloggled被取消,那么您就已经提到了问题。因此,您需要重新解释才能不启用特定选项。当您不知道如何处理选项时,很难提出建议。

尽管如此,我可能会做类似的事情

struct Options: OptionSet {
   let rawValue: Int

   static let floogled = Options(rawValue: 1 << 0)
   static let jibjabbed = Options(rawValue: 1 << 1)
}

enum OptionsComplement {
   case notFloogled
   case notJibjabbed
}

func foo(options: Options = [.floogled, .jibjabbed]) {
   ...
}

func foo(options: [OptionsComplement] = []) {
   var optionsEnabled: Options = []() //whatever type the array is
   if !options.contains(.notFloggled) {
       optionsEnabled.append(.floogled)
   }
   if !options.contains(.notJibjabbed) {
       optionsEnabled.append(.jibjabbed)
   }
   foo(options: optionsEnabled)
}

答案 2 :(得分:-1)

我想,如果我知道,我将总是通过否定样式的选项(notFloogled / notJibjabbed),仅以肯定的方式阅读,我可以:

  1. 使用负面定义的选项集:
struct Options: OptionSet {
   let rawValue: Int

   static let notFloogled = Options(rawValue: 1 << 0)
   static let notJibjabbed = Options(rawValue: 1 << 1)
}
  1. 使用getter(甚至setter)扩展它以检查肯定定义的选项:
extension Options {
   var isFloogled: Bool {
      return !self.contains(.notFloogled)
   }

   var isJibjabbed: Bool {
      return !self.contains(.notJibjabbed)
   }
}
  1. 定义检查阳性指标的功能:
func foo(options: Options = []) {
   if options.isFloogled {
      // Floogle the foo
   }
   ...
}
  1. 仍然使用带有否定定义的选项来调用函数:
foo() // With all options enabled
foo(options: .notFloogled) // With floogling disabled
foo(options: [.notFloogled, .notJibjabbed]) // With floogling jibjabbing disabled