To me Angle is a unit with two common representations: radians and degrees.
It's not an enum because it doesn't have two values, it has one value that
you can view in two ways.
Therefore I would make an Angle struct, something like:
//: Angle struct instead of angle enum
import Foundation
struct Angle {
static let d2R = Double.pi / 180
static let r2D = 180 / Double.pi
private var degs: Double
var degrees: Double {
return degs
}
var radians: Double {
return degs * Angle.d2R
}
init(degrees: Double = 0) {
degs = degrees.truncatingRemainder(dividingBy: 180)
}
init(radians: Double) {
self.init(degrees: radians * Angle.r2D)
}
}
extension Angle: Hashable {
var hashValue: Int {
return degs.hashValue
}
static func ==(lhs: Angle, rhs: Angle) -> Bool {
return lhs.degs == rhs.degs
}
}
extension Angle/*: FloatingPoint*/ {
static func +(lhs: Angle, rhs: Angle) -> Angle {
return Angle(degrees: lhs.degs + rhs.degs)
}
static func +=(lhs: inout Angle, rhs: Angle) {
lhs.degs += rhs.degs
lhs.degs = lhs.degs.truncatingRemainder(dividingBy: 180)
}
// Rest of FloatingPoint ...
}
// Trig
extension Angle {
var sin: Double {
return Foundation.sin(radians) // Need to qualify name to stop name
clash
}
// Other trig
}
let a = Angle(degrees: 90) // 90
a.radians // pi / 2
a.sin // 1
let b = Angle(radians: 3) // Almost pi
b.degrees // 171.9
let c = a + b // Wraps over 180 degrees
c.degrees // 81.9
c == b // false
c.hashValue // 463...
let d = Angle(degrees: -90) // -90
d.radians // -pi / 2
var e = Angle(radians: -3) // Almost -pi
e.degrees // -171.9
e += d // Wraps over -180 degrees
e.degrees // -81.9
-- Howard.
···
On 19 June 2017 at 13:32, David Sweeris via swift-users < swift-users@swift.org> wrote:
On Jun 18, 2017, at 19:30, Nevin Brackett-Rozinsky via swift-users < > swift-users@swift.org> wrote:
Is there a way to restrict the associated values of an enum? For example,
suppose I have this type:
enum Angle {
case radians(Double)
case degrees(Double)
}
I want to ensure that the radians values is always in [0, 2π) and the
degrees values is always in [0, 360). Ideally I would like to write an
initializer which is called when the user writes eg. “let x: Angle =
.degrees(-45)” and contains the logic to wrap the provided value into the
allowed range (in this case by adding a multiple of 360).
I don’t see a way to do it. Is this possible?
The closest I’ve found is to create auxiliary types such as
struct Degree { … }
struct Radian { … }
and give them appropriate initializers, then use them for the associated
values. However that is undesirable because it adds an extra level of depth
to get at the actual numeric values.
Is there a better way?
Not off the top of my head, at least not without changing the syntax.
You could add two inits, with different argument labels, and not directly
set the case: “let x = Angle(degrees: -45)”
You could add "public var radians: Double { get {...} set {...} }" (and
again for "degrees") to `Angle`. The getter would need to switch on self
to figure out if you need to convert between the two (like if someone said
".radians(2).degrees"): “var x = Angle.radians(0); x.degrees = -45"
Alternately, you could add "subscript() -> Double" and switch on self in
both the setter and the getter to figure out if you should be wrapping
at 2π or 360 and to do any conversions: “var x: Angle = .radians(0); x =
-45"
Hope that helps
- Dave Sweeris
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users