Быстрые дженерики: требуется возможность добавления и умножения типа
Я пытаюсь использовать некоторые примеры из книги 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)]