Reputation: 22939
From How to customize ternary operators in Swift I know that it's possible to create custom ternary operators using two binary operators, my question is:
Is there a way I could use it for initialisation of classes or structs?
Say I have a LinearEquation
. Everything works fine but initialising an instance doesn't feel very natural. Here's how it works at the moment:
struct LinearEquation {
var m: Double
var c: Double
func of(x: Double) -> Double {
return m * x + c
}
}
let f = LinearEquation(m: 2, c: 1)
f.of(2) // returns 5
Is there a way I could create a LinearEquation
by writing let f = m * x + c
? Would it also be possible to omit the + c
if the line passed through the origin?
(I've given an answer below but I was wondering if anyone had any other suggestions for the reason stated at the end of my answer)
Upvotes: 1
Views: 634
Reputation: 539695
I would choose a slightly different approach. With your solution, you get the unexpected result:
let f1 = x * 2 + (1 + 2)
println(f1.of(1)) // 5.0 (correct)
let f2 = x * 2 + 1 + 2
println(f2.of(1)) // 4.0 (What ??)
And
let f3 = { println("foo") } * 2
compiles without making sense.
I would define the linear function "x" as a static member
(and m
, c
as constant properties):
struct LinearEquation {
let m: Double
let c: Double
func of(x: Double) -> Double {
return m * x + c
}
static let X = LinearEquation(m: 1.0, c: 0.0)
}
and addition and multiplication as
func * (lhs: LinearEquation, rhs: Double) -> LinearEquation {
return LinearEquation(m: lhs.m * rhs, c: lhs.c * rhs)
}
func + (lhs: LinearEquation, rhs: Double) -> LinearEquation {
return LinearEquation(m: lhs.m, c: lhs.c + rhs)
}
Then
let f1 = LinearEquation.X * 2 + 1 + 2
println(f1.of(1)) // 5.0
works as expected. And with
extension LinearEquation : FloatLiteralConvertible {
init(floatLiteral value: Double) {
self = LinearEquation(m: 0.0, c: value)
}
}
you can define a constant function simply as
let f2 : LinearEquation = 2.0
println(f2.of(3)) // 2.0
Upvotes: 1
Reputation: 22939
I wouldn't use that syntax in all situations because it's not entirely clear what type f
is. You could instead write:
let f: LinearEquation = m * x + c
It's not quite as succinct, however it makes the intentions clearer. Anyway, onto my answer!
Firstly, Looking at the example: if you break down the statement f = m * x + c
, it makes sense for m * x + c
to return a LinearEquation
which is then assigned to f
. The problem then becomes overloading *
and +
to create a LinearEquation
:
Secondly, x
, in this case, doesn't actually provide any information in the creation of a LinearEquation
; it's just a placeholder for an x
value that will be used later. Therefore I'll declare x
, at global scope, like so:
func x() {} // Yup, does nothing!
Thirdly, you need to consider the order in which the operators are evaluated. Therefore x * m
will be evaluated first and the + c
will be evaluated second. This means x * m
could return a LinearEquation
with no y-intercept. Then the +
operator could take the LinearEquation
, add the intercept and return the return the complete LinearEquation
. Here are the operators:
// lhs is where you pass x, defined earlier.
func * (lhs: () -> Void, rhs: Double) -> LinearEquation {
return LinearEquation(m: rhs, c: 0.0)
}
func + (var lhs: LinearEquation, rhs: Double) -> LinearEquation {
lhs.c = rhs
return lhs
}
Lastly, now you can create a LinearEquation
like so:
let f = x * 2 + 1
f.of(2) // returns 5
Or if it passes through the origin:
let f = x * 2
f.of(2) // returns 4
The one main problem I can think of is not being able to use x
as a variable now. So if anyone has a better solution it would be great to hear.
Upvotes: 0