ABakerSmith
ABakerSmith

Reputation: 22939

Ternary Operators For Initialisation In Swift

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

Answers (2)

Martin R
Martin R

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

ABakerSmith
ABakerSmith

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

Related Questions