Могу ли я привязать различные связанные значения в перечислении Swift к одному и тому же var?
Я сделал небольшое перечисление Angle, чтобы можно было программировать с различными сменными угловыми форматами:
enum Angle {
case Radians(CGFloat)
case Degrees(CGFloat)
case Rotations(CGFloat)
}
Я нахожу, что есть некоторый избыточный шаблонный код с этим подходом все же. Например, я хотел добавить вычисляемую переменную, которая только что вернула необработанный базовый связанный float:
var raw:CGFloat {
switch self {
case let .Radians(value):
return value
case let .Degrees(value):
return value
case let .Rotations(value):
return value
}
}
Я попытался изменить это, чтобы прочитать:
case .Radians(let value), .Degrees(let value):
return value
Я надеялся, что это будет достаточно умно, чтобы понять, что он решит только один матч, поэтому конфликт был проигнорированным. Но нет такого устройства. Компилятор говорит, что не может разрешить конфликт между двумя let value
заявления.
Так есть ли какая-то идиоматическая хитрость, которую я еще не обнаружил с помощью перечисления Swift, которая позволила бы мне не повторяться там так много?
Другой похожий пример, когда я реализовал *
функция:
func * (lhs:Angle, rhs:CGFloat) -> Angle {
switch lhs {
case .Radians:
return .Radians(lhs.raw * rhs)
case .Degrees:
return .Degrees(lhs.raw * rhs)
case .Rotations:
return .Rotations(lhs.raw * rhs)
}
}
Кажется, должен быть более простой способ выразить это: "Сделайте тот же тип enum с моим связанным значением, умноженным на скалярный аргумент".
(Я не уверен, что у меня есть хороший заголовок по этому вопросу, не стесняйтесь улучшать / предлагать лучше)
2 ответа
Это интересный случай: перечисление как-то не является правильным типом данных, потому что значение не в радианах или градусах, это просто углы, а не разные вещи. Также typealias Radians = Double
не будет работать, потому что нет безопасности устройства.
Может быть, вы можете использовать что-то вроде этого, хотя:
import Darwin
struct Angle {
enum Unit {
case Radians
case Degrees
case Rotations
var radiansFactor : Double {
switch self {
case Radians: return 1
case Degrees: return 180.0 / M_PI
case Rotations: return 1 / 2 / M_PI
}
}
}
var unit : Unit {
didSet {
value /= oldValue.radiansFactor
value *= unit.radiansFactor
}
}
var value : Double
}
func * (var lhs: Angle, rhs: Double) -> Angle {
lhs.value *= rhs
return lhs
}
var angle = Angle(unit: .Degrees, value: 180)
angle.value // 180.0
angle.unit = .Radians
angle.value // 3.141592...
angle.unit = .Rotations
angle.value // 0.5
Да, и что касается ответа на ваш первоначальный вопрос: нет, вы не можете.
В более новых версиях Swift это теперь возможно, если значения одного типа:
enum Foo {
case bar(Int), bas(Int)
}
var this = .random() ? Foo.bar(5) : Foo.bas(5)
switch this {
case .bar(let foo), .bas(let foo): print(foo) // Always prints: 5
}
Вы даже можете сделать это:
enum Foo {
case bar(Int, Int), bas(Int, Int)
}
var this = .random() ? Foo.bar(5) : Foo.bas(5)
switch this {
case .bar(let foo, let bat), .bas(let foo, let bat): print(foo, bat) // Always prints: 5 5
}
Основываясь на ответе @Kametrixom, то, что я в итоге сделал для этой конкретной проблемы моделирования "Угол", было следующим:
import UIKit
import CoreGraphics
let Tau = CGFloat(2 * M_PI)
struct Angle {
enum Unit:CGFloat {
case Radians = 1.0
case Degrees = 57.29577951309314 // 360.0 / Tau
case Rotations = 6.28318530717959 //Tau
}
var raw:CGFloat = 0.0
var unit:Unit = .Radians
func convert(anotherUnit:Unit) -> Angle {
return self.unit == anotherUnit ? self : Angle(raw: self.raw * self.unit.rawValue / anotherUnit.rawValue, unit: anotherUnit)
}
var radians:Angle {
return self.convert(.Radians)
}
var degrees:Angle {
return self.convert(.Degrees)
}
var rotations:Angle {
return self.convert(.Rotations)
}
var cos:CGFloat {
return CoreGraphics.cos(self.radians.raw)
}
var sin:CGFloat {
return CoreGraphics.sin(self.radians.raw)
}
var half:Angle {
return self / 2
}
}
extension Angle: Comparable {}
func < (lhs:Angle, rhs:Angle) -> Bool {
return lhs.radians.raw < rhs.radians.raw
}
func == (lhs:Angle, rhs:Angle) -> Bool {
return lhs.radians.raw == rhs.radians.raw
}
func + (lhs:Angle, rhs:Angle) -> Angle {
let other = rhs.convert(lhs.unit)
return Angle(raw: lhs.raw + other.raw, unit: lhs.unit)
}
func - (lhs:Angle, rhs:Angle) -> Angle {
let other = rhs.convert(lhs.unit)
return Angle(raw: lhs.raw - other.raw, unit: lhs.unit)
}
func * (lhs:CGFloat, rhs:Angle) -> Angle {
return rhs * lhs
}
func * (lhs:Angle, rhs:CGFloat) -> Angle {
return Angle(raw: lhs.raw * rhs, unit: lhs.unit)
}
func / (lhs:Angle, rhs:CGFloat) -> Angle {
return Angle(raw: lhs.raw / rhs, unit: lhs.unit)
}