Перегрузка операторов кортежами в Swift - PullRequest
0 голосов
/ 11 мая 2018

Перегрузка оператора

Swift 4.1, Xcode 9.3

Я пытаюсь создать функцию квадратного уравнения в Swift.

Работая над этим, я обнаружил, что мне нужно перегрузить некоторые операторы, чтобы я мог работать с кортежами наряду с другими числами (в данном случае Double), это потому, что мне нужно было использовать свой пользовательский ± оператор.Несмотря на то, что я работал только со значением типа Double в своей квадратичной функции, я решил, что хочу использовать обобщенные элементы, чтобы сделать мои перегруженные операторы более гибкими для будущего использования.

Но -по причине, которую я не понимаю - я получаю ошибки, связанные с объявлением перегруженного оператора /.


Пользовательский ± Оператор - Работает

infix operator ± : AdditionPrecedence

public func ± <T: Numeric>(left: T, right: T) -> (T, T) {
    return (left + right, left - right)
}

Квадратичная функция - Рабочая

func quadratic(a: Double, b: Double, c: Double) -> (Double, Double) {
    return (-b ± sqrt((b * b) - (4 * a * c))) / (2 * a)
}

Перегруженные операторы - Частично работает¹

//Binary operator '/' cannot be applied to two 'T' operands
func / <T: Numeric>(lhs: (T, T), rhs: T) -> (T, T) { 
    return (lhs.0 / rhs, lhs.1 / rhs)
}
func * <T: Numeric>(lhs: (T, T), rhs: T) -> (T, T) {
    return (lhs.0 * rhs, lhs.1 * rhs)
}
func - <T: Numeric>(lhs: (T, T), rhs: T) -> (T, T) {
    return (lhs.0 - rhs, lhs.1 - rhs)
}
func + <T: Numeric>(lhs: (T, T), rhs: T) -> (T, T) {
    return (lhs.0 + rhs, lhs.1 + rhs)
}

//Binary operator '/' cannot be applied to two 'T' operands
func / <T: Numeric>(lhs: T, rhs: (T, T)) -> (T, T) {
    return (lhs / rhs.0, lhs / rhs.1)
}
func * <T: Numeric>(lhs: T, rhs: (T, T)) -> (T, T) {
    return (lhs * rhs.0, lhs * rhs.1)
}
func - <T: Numeric>(lhs: T, rhs: (T, T)) -> (T, T) {
    return (lhs - rhs.0, lhs - rhs.1)
}
func + <T: Numeric>(lhs: T, rhs: (T, T)) -> (T, T) {
    return (lhs + rhs.0, lhs + rhs.1)
}

1. Я получаю только этиошибки оператора /, а не других перегруженных операторов (+, - или *).


перегруженные операторы с ошибками (/ s) - не работает

//Binary operator '/' cannot be applied to two 'T' operands
func / <T: Numeric>(lhs: (T, T), rhs: T) -> (T, T) { 
    return (lhs.0 / rhs, lhs.1 / rhs)
}

//Binary operator '/' cannot be applied to two 'T' operands
func / <T: Numeric>(lhs: T, rhs: (T, T)) -> (T, T) {
    return (lhs / rhs.0, lhs / rhs.1)
}

Гипотеза: Я думаю, что тот факт, что я использую оператор / в декларациисам перегруженный оператор / - несмотря на то, что он используется в рекламеifferent context - вызывает ошибку.


Последний вопрос:

Как мне разрешить ошибки, сохраняя при этом гибкость моих перегруженных операторов?


Бонусный вопрос 1: Если я могу (я думаю, что мне, вероятно, придется вернуть String ввыполнить это), я также хотел бы сделать отдельную квадратичную функцию, которая может возвращать точное решение?

Бонус Вопрос 2: ТакжеЕсли кто-нибудь знает, как создать отдельную функцию, которая может решать уравнение кубического , а не квадратичного, это также будет оценено.


Ответы [ 2 ]

0 голосов
/ 11 мая 2018

Ключ должен определять /, когда T равен FloatingPoint или BinaryInteger, а не просто Numeric.Кроме того, я бы порекомендовал вам использовать перечисление, чтобы правильно смоделировать три возможных результата квадратичной функции:

import Foundation

infix operator ± : AdditionPrecedence

public enum QuadraticRoots<T> {
    case two(T, T)
    case one(T)
    case none

    func applyWithSelfOnLeft(_ fn: (T, T) -> T, withOperand value: T) -> QuadraticRoots {
        switch self {
            case let .two(a, b): return .two(fn(a, value), fn(b, value))
            case let .one(a): return .one(fn(a, value))
            case .none: return .none
        }
    }

    func applyWithSelfOnRight(withOperand value: T, _ fn: (T, T) -> T) -> QuadraticRoots {
        switch self {
            case let .two(a, b): return .two(fn(value, a), fn(value, b))
            case let .one(a): return .one(fn(value, a))
            case .none: return .none
        }
    }
}

public func ± <T: Numeric>(left: T, right: T) -> QuadraticRoots<T> {
    return QuadraticRoots.two(left + right, left - right)
}

extension QuadraticRoots where T: Numeric {

    static func + (quadRoots: QuadraticRoots, rhs: T) -> QuadraticRoots {
        return quadRoots.applyWithSelfOnLeft((+), withOperand: rhs)
    }
    static func - (quadRoots: QuadraticRoots, rhs: T) -> QuadraticRoots {
        return quadRoots.applyWithSelfOnLeft((-), withOperand: rhs)
    }
    static func * (quadRoots: QuadraticRoots, rhs: T) -> QuadraticRoots {
        return quadRoots.applyWithSelfOnLeft((*), withOperand: rhs)
    }


    static func + (lhs: T, quadRoots: QuadraticRoots) -> QuadraticRoots {
        return quadRoots.applyWithSelfOnRight(withOperand: lhs, (+))
    }
    static func - (lhs: T, quadRoots: QuadraticRoots) -> QuadraticRoots {
        return quadRoots.applyWithSelfOnRight(withOperand: lhs, (-))
    }
    static func * (lhs: T, quadRoots: QuadraticRoots) -> QuadraticRoots {
        return quadRoots.applyWithSelfOnRight(withOperand: lhs, (*))
    }

    static func discriminantOf(xSquared a: T, x b: T, c: T) -> T { return b*b - 4*a*c }
}

extension QuadraticRoots where T: FloatingPoint {
    static func / (quadRoots: QuadraticRoots, rhs: T) -> QuadraticRoots {
        return quadRoots.applyWithSelfOnLeft((/), withOperand: rhs)
    }

    static func / (lhs: T, quadRoots: QuadraticRoots) -> QuadraticRoots {
        return quadRoots.applyWithSelfOnRight(withOperand: lhs, (/))
    }

    static func solveFrom(xSquared a: T, x b: T, c: T) -> QuadraticRoots {
        let discriminant = self.discriminantOf(xSquared: a, x: b, c: c)
        return (-b ± sqrt(discriminant)) / (2 * a)
    }
}

let a = 5 ± 10
print(a)
print(a + 2)
print(a - 2)
print(a * 2)

print(2 + a)
print(2 - a)
print(2 * a)

//print(a / 2)
0 голосов
/ 11 мая 2018

Numeric объявляет только операторы +, - и *Numeric нет /.Вам нужно либо BinaryInteger, либо FloatingPoint, чтобы получить деление.

Для решателя квадратичных уравнений я бы сказал, что FloatingPoint более уместно:

func / <T: FloatingPoint>(lhs: T, rhs: (T, T)) -> (T, T) {
    return (lhs / rhs.0, lhs / rhs.1)
}

func / <T: FloatingPoint>(lhs: (T, T), rhs: T) -> (T, T) {
    return (lhs.0 / rhs, lhs.1 / rhs)
}

Как сказал Александр, было бы лучше, если бы вы создали перечисление, представляющее решения:

enum QuadraticSolution {
    case none
    case one(value: Double)
    case two(value1: Double, value2: Double)
}

func quadratic(a: Double, b: Double, c: Double) -> QuadraticSolution {
    let discriminant = (b * b) - (4 * a * c)
    switch discriminant {
    case 0:
        return .one(value: -b / (2 * a))
    case let x where x < 0:
        return .none
    default:
        let twoSolutions = (-b ± sqrt(discriminant)) / (2 * a)
        return .two(value1: twoSolutions.0, value2: twoSolutions.1)
    }
}
...