Быстрые дженерики: требуется возможность добавления и умножения типа

Я пытаюсь использовать некоторые примеры из книги Swift, а именно пример матрицы, который содержит параметры подстроки. Это код, который у меня есть:

struct Matrix<T> {
    let rows: Int, columns: Int
    var grid: T[]

    var description: String {
        return "\(grid)"
    }

    init(rows: Int, columns: Int, initialValue: T) {
        self.rows = rows
        self.columns = columns
        grid = Array(count: rows * columns, repeatedValue: initialValue)
    }

    func indexIsValidForRow(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }

    subscript(row: Int, column: Int) -> T {
        get {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}

В основном это копируется из книги. Основное отличие здесь в этой строке:

struct Matrix<T>

Насколько я могу судить, это говорит компилятору, что мой класс Matrix может хранить значения типа T, заданные кодом, используя этот класс. Теперь я хотел бы убедиться, что тип T можно сравнить, поэтому я могу написать это:

struct Matrix<T: Equatable>

Это может быть полезно, если я хочу сравнить 2 матрицы, что означало бы сравнение их значений. Я также хочу предоставить возможность суммировать две матрицы, поэтому я должен добавить к этой строке протокол, требующий, чтобы тип T был задан пользователем матрицы:

struct Matrix<T: Equatable, "Summable">

Аналогично, я также хотел бы сказать:

struct Matrix<T: Equatable, "Summable", "Multipliable">

Вопрос 1: Какое имя протокола я могу использовать? Как я могу достичь этого?

В соответствующей заметке, чтобы добавить возможности добавления с помощью оператора '+', я должен объявить такую ​​функцию (это также относится к умножению):

@infix func + (m1: Matrix<T>, m2: Matrix<T>) -> Matrix<T> {
    // perform addition here and return a new matrix
    return result
}

Однако этот код не принят Xcode. Более конкретно, этот ) -> Matrix<T> { вызывает ошибку: Use of undeclared type 'T'. То, что я подразумеваю под этим <T>, состоит в том, что результатом будет матрица, которая имеет тот же тип двух входных матриц, но я, вероятно, полностью заполняю синтаксис.

Вопрос 2: Как я могу предоставить информацию о типе в результате добавления?

Ответы

Ответ 1

Здесь для вашего второго вопроса (но вы действительно должны задать два отдельных вопроса):

@infix func + <T> (m1: Matrix<T>, m2: Matrix<T>) -> Matrix<T> { ... }

Для вашего первого вопроса: перед его решением здесь синтаксис для определения нескольких ограничений для параметра типа:

struct Matrix<T where T: Equatable, T: Summable, T: Multipliable> {...}

или, как пишет GoZoner в комментариях:

struct Matrix<T: protocol<Equatable, Summable, Multipliable>> {...}

Но нам это не понадобится. Сначала определите новый протокол и перечислите необходимые операции. Вы даже можете расширить его Equatable:

protocol SummableMultipliable: Equatable {
    func +(lhs: Self, rhs: Self) -> Self
    func *(lhs: Self, rhs: Self) -> Self
}

Затем укажите расширения для типов, которые вы хотите согласовать. Здесь, для Int и Double, расширения являются пустыми, поскольку реализация необходимых операционных систем встроена:

extension Int: SummableMultipliable {}
extension Double: SummableMultipliable {}

Затем объявите ограничение типа для параметра типа:

struct Matrix<T: SummableMultipliable> { ... }

Наконец, вы можете написать следующее:

let intMat = Matrix<Int>(rows: 3, columns: 3, initialValue: 0)
let doubleMat = Matrix<Double>(rows: 3, columns: 3, initialValue: 0)
let i: Int = intMat[0,0]
let d: Double = doubleMat[0,0]

Последнее, что вам нужно, это вставить ограничение типа в определение вашего оператора:

@infix func + <T: SummableMultipliable> (m1: Matrix<T>, m2: Matrix<T>) -> Matrix<T> { ... }

Ответ 2

Для начала вопроса 1, определяя протокол

protocol Summable { func ignore () }

Он имеет метод выброса. Затем добавьте его как расширение к вещам, которые вы хотите суммировать.

extension Int: Summable { func ignore () {} }

[Примечание: я попробовал вышеописанный метод выброса, но получил сбой; Я подозреваю, что Свифт нуждался в чем-то, что-то в protocol.]

Теперь тест

 35> protocol Summable { func ignore () }
 36> extension Int: Summable { func ignore () {} }
 37> func testing<T: Summable> (x: T) -> T { return x }
 38> testing(1)
$R16: (Int) = 1
 39> testing(1.2)
<REPL>:39:1: error: cannot convert the expression type '$T1' to type 'Summable'
testing(1.2)
^~~~~~~~~~~~

Для Вопроса 2, [edit] Используйте следующие

@infix func +<T: Summable> (m1: Matrix<T>, m2: Matrix<T>) -> Matrix<T> { ... }

[Примечание: я пробовал выше в REPL, который не работал. Но он работает в файле (возможно, определяет "глобальную среду", которую не имеет REPL)]