Отправить значение аргумента по умолчанию в функцию?

TL;DR

Если у меня есть func show(message: String = "Hello"), как мне передать аргумент по умолчанию без пропуска имени параметра? (например, show(message: default))

Примечание: show() это не то, что я ищу! Подробнее почему.


Представьте, что у нас определена следующая функция:

func makeCreature(color: UIColor, eyeCount: Int = 2, noseCount: Int = 1) -> Creature {
  // ...
}

Затем у нас также определен еще один метод, makeCreatures:

func makeCreatures(count: Int, color: UIColor) {
  for 1...count {
    makeCreature(color: color)
  }
}

Тем не менее, теперь мы хотим легко настроить eyeCount и NoseCount для makeCreatures. Один из способов сделать это — переопределить аргументы и их значения по умолчанию:

Решение №1

func makeCreatures(count: Int, color: UIColor, eyeCount: Int = 2, noseCount: Int = 1) {
  for 1...count {
    makeCreature(color: color, eyeCount: eyeCount, noseCount: noseCount)
  }
}

Проблема в том, что если количество глаз по умолчанию когда-либо изменится, мне нужно помнить, чтобы обновить его в двух местах: makeCreature и makeCreatures.

Вместо этого я хотел бы определить метод как:

func makeCreatures(count: Int, color: UIColor, eyeCount: Int? = nil, noseCount: Int? = nil)

Однако теперь это означает, что мне нужно создать 4 разные ветки if:

Решение №2

func makeCreatures(count: Int, color: UIColor, eyeCount: Int? = nil, noseCount: Int? = nil) {
  for 1...count {
    if let eyeCount = eyeCount, let noseCount = noseCount {
      makeCreature(color: color, eyeCount: eyeCount, noseCount: noseCount) 
    } else if let eyeCount = eyeCount {
      makeCreature(color: color, eyeCount: eyeCount)
    } else if let noseCount = noseCount {
      makeCreature(color: color, noseCount: noseCount)
    } else {
      makeCreature(color: color)
    }
  }
}

Необходимость создавать 4 разные ветки немного уродлива и трудна для понимания. Есть ли лучший способ, который дает мне лаконичность решения № 1 с СУХОЙ № 2? Что-то похожее на это:

Идеальное решение?

func makeCreatures(count: Int, color: UIColor, eyeCount: Int? = nil, noseCount: Int? = nil) {
  for 1...count {
    makeCreature(color: color, 
      eyeCount: eyeCount ?? default, 
      noseCount: noseCount ?? default)
  }
}

Где default означает использование значения аргумента по умолчанию, как определено в makeCreature (т. е. 2 для eyeCount и 1 для noseCount).

Если нет, то какие другие решения могут помочь мне достичь этой цели?


person Senseful    schedule 29.10.2016    source источник
comment
Просто установите две константы Int в некоторой области, доступной для обеих функций, и сделайте их аргументами функций по умолчанию. Нет необходимости в дополнительных опциях.   -  person BallpointBen    schedule 29.10.2016
comment
Мне любопытно, почему ваш makeCreature не является инициализатором Creature.   -  person Alexander    schedule 29.10.2016


Ответы (2)


Для полноты картины есть другое решение, похожее на от Александра.

Вы можете создать структуру для хранения атрибутов существа:

struct Attributes {
  let color: UIColor
  let eyeCount: Int = 2
  let noseCount: Int = 1
}

Затем переопределите функции, чтобы они принимали атрибуты:

func makeCreature(attributes: Attributes) -> Creature {
  // ...
}

func makeCreatures(count: Int, attributes: Attributes) {
  for 1...count {
    makeCreature(color: color, attributes: attributes)
  }
}

Что позволяет использовать значения по умолчанию с обеими функциями:

// uses 2 eyes (default), and 2 noses
makeCreature(attributes: Attributes(color: .purple, noseCount: 2))

// use 10 eyes, and 1 nose (default)
makeCreatures(count: 3, attributes: Attributes(color: .blue, eyeCount: 10))
person Senseful    schedule 29.10.2016

Это не совсем то, что вы ищете, но для этого вы можете использовать шаблон построителя:

struct Creature {
    let color: UIColor
    let eyeCount: Int
    let noseCount: Int
}

struct CreatureBuilder {
    var color: UIColor?
    var eyeCount: Int = 2 //store defaults here
    var noseCount: Int = 1

    func build() -> Creature {
        guard let color = color else { fatalError("Creatures need a color!") }

        return Creature(color: color, eyeCount: eyeCount, noseCount: noseCount)
    }
}

func makeCreatures(count: Int, color: UIColor, eyeCount: Int? = nil, noseCount: Int? = nil) {
    var builder = CreatureBuilder()
    builder.color = color;
    if let eyeCount = eyeCount { builder.eyeCount = eyeCount } //override defaults only for non-nil params
    if let noseCount = noseCount { builder.noseCount = noseCount }

    for _ in 1...count {
        let creature = builder.build() //TODO: do something with the creature.
    }
}
person Alexander    schedule 29.10.2016