OptionSet с перевернутыми опциями - PullRequest
0 голосов
/ 14 февраля 2019

Скажем, у меня есть набор параметров, которые я могу передать определенным процессам, например,

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().Пока все хорошо.

Однако в исключительных случаях, когда я не хочу вызывать эти функции со всеми включенными параметрами, использование было бы намного более интуитивно понятным, если бы я мог указать, какие параметры не, чтобы включить, а не какие опции остаются включенными.Например, я бы предпочел позвонить 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]), я получаю опцию, установленную с включенными и floogling и jibjabbing вместо или .

Есть ли другой способделать то, что я пытаюсь достичь, сохраняя реализацию и использование максимально интуитивно понятным?

Ответы [ 3 ]

0 голосов
/ 14 февраля 2019

OptionSet - это set .У него есть алгебра (SetAlgebra).Так что вам не нужны «перевернутые опции»; вы инвертировать.Чтобы инвертировать, просто вычтите из себя случай «все».

Для иллюстрации (используя собственный пример OptionSet от Apple):

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`
0 голосов
/ 14 февраля 2019

Это немного сбивает с толку.Итак, эти опции исключают друг друга?Потому что ваше дополнение .notFloogled говорит, что это тогда .jibjiabbed (в последнем примере).Но вверху вы можете включить обе опции.Что делает его более сложным, так это то, что если в вашей структуре есть обе переменные .floogled и .notFloogled, вы можете добавить обе к параметру массива в foo() и затем какую опцию обработать.

Toрешить проблему с помощью дополнения: я предполагаю, что необработанное значение говорит, какую опцию включить в двоичном коде.Так что 1 означает включение, а 0 - ничего не делать.В случае использования дополнения с .notFloogled я бы переосмыслил двоичный код и сказал бы, что 1 notEnable, а 0 ничего не делать.В противном случае, если вы скажете, что notFloggled является jibjabbed, у вас есть проблема, о которой вы уже упоминали.Таким образом, вам нужна новая интерпретация, чтобы не включить конкретную опцию.Трудно делать предположения, когда вы не знаете, как обрабатываются Options.

Тем не менее, я бы сделал что-то подобное, может быть

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)
}
0 голосов
/ 14 февраля 2019

Полагаю, что если я знаю , что я всегда передаю параметры в отрицательном стиле (notFloogled / notJibjabbed), и только читают их в положительном стиле, я мог бы:

  1. Использовать отрицательно определенный набор опций:
struct Options: OptionSet {
   let rawValue: Int

   static let notFloogled = Options(rawValue: 1 << 0)
   static let notJibjabbed = Options(rawValue: 1 << 1)
}
Дополните его геттерами (и даже сеттерами) для проверки положительно определенных опций:
extension Options {
   var isFloogled: Bool {
      return !self.contains(.notFloogled)
   }

   var isJibjabbed: Bool {
      return !self.contains(.notJibjabbed)
   }
}
Определите функции для проверки положительных показателей:
func foo(options: Options = []) {
   if options.isFloogled {
      // Floogle the foo
   }
   ...
}
По-прежнему вызывать функции с отрицательно определенными параметрами:
foo() // With all options enabled
foo(options: .notFloogled) // With floogling disabled
foo(options: [.notFloogled, .notJibjabbed]) // With floogling jibjabbing disabled
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...