Swift Generics ... Проверка соответствия протоколу с соответствующим типом - PullRequest
0 голосов
/ 09 апреля 2019

Я пытаюсь написать универсальную функцию в Swift, которая принимает любое число, Int, Float, Double и т. Д., Устанавливая универсальный тип в <T: Numeric>. Таким образом,

func doSomething<T: Numeric>(with foo: T, and bar: T) -> T {
  // body
}

Большая часть тела будет работать для любого типа Numeric, но по пути мне нужно найти остаток ... что означает, что мне нужен другой подход для FloatingPoint типов по сравнению с Integer типами.

Когда T является Int, это означает использование оператора по модулю:

let remainder = foo % bar

Однако, когда T - это Double или Float, оператор по модулю не работает, и мне нужно использовать метод truncatingRemainder(dividingBy:):

let remainder = foo.truncatingRemainder(dividingBy: bar)

Я борюсь за то, чтобы найти способ отсеять это. Теоретически, я должен быть в состоянии сделать что-то вроде этого:

var remainder: T
if T.self as FloatingPoint { // <- This doesn't work
  remainder = foo.truncatingRemainder(dividingBy: bar)
} else {
  remainder = foo % bar
}

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

error: protocol 'FloatingPoint' can only be used as a generic constraint because it has Self or associated type requirements

Я понимаю эту ошибку ... по сути, FloatingPoint является универсальным с все еще универсальным связанным типом для определения принимающего типа.

Однако я хотел бы знать, как лучше всего условно запустить выбранные блоки кода, которые применяются только к более узко определенному протоколу, чем тот, который определен с помощью общего параметра (T).

В частности, есть ли способ определить одну универсальную функцию для обработки всех типов Numeric, а затем дифференцировать по FloatingPoint против Integer типов внутри.

Ответы [ 2 ]

1 голос
/ 10 апреля 2019

Это не похоже на хороший вариант использования для универсального. Вы заметите, что операторы типа + не являются универсальными в Swift. Они используют перегрузку , и вы тоже должны.

1 голос
/ 10 апреля 2019

Здесь есть несколько проблем.

  1. Числовой - это неправильный протокол, если вы хотите взять остатки.

Если я неправильно читаю документациюдля Числового тип матрицы мог бы разумно соответствовать Числовому.Если бы в вашу функцию была передана матрица, у вас не было бы реального способа вычислить остатки, потому что это не очень четкое понятие.Следовательно, ваша функция не должна быть определена для всех числовых типов.Решение состоит в том, чтобы определить новый протокол, который описывает типы с четко определенными остатками.К сожалению, как отмечает Александр в комментариях ...

Swift не позволит вам расширить протокол для соответствия другому протоколу.

Существуют различные технические причины этого ограничения, в основном связанные с трудностями, когда тип соответствует протоколу в несколькихпути.

Я думаю, у вас есть два разумных решения.

A.Сделайте две различные перегрузки doSomething, одну с T: FloatingPoint, а другую с T: BinaryInteger.Если между этими реализациями слишком много общего кода, вы можете преобразовать doSomething в несколько функций, большинство из которых будут определены для всех Numeric.

B.Введите новый протокол RemainderArithmetic: Numeric, который описывает необходимые операции.Затем напишите явные соответствия для всех конкретных типов, которые вы хотите использовать, например, extension Double: RemainderArithmetic и extension UInt: RemainderArithmetic.

Ни одно из этих решений не особенно привлекательно, но я думаю, что у них есть одно ключевое преимущество: оба эти решенияпроясните конкретную семантику ожидаемых типов.Вы на самом деле не ожидаете типы, отличные от BinaryInteger или FloatingPoint, поэтому вы не должны принимать другие типы.Семантика остатков может быть чрезвычайно хитрой, о чем свидетельствует широкий диапазон поведения, описанного на странице википедии для мода .Поэтому, вероятно, не очень естественно определять функцию одинаково для целых чисел и чисел с плавающей запятой.Если вы уверены, что именно это вы и хотите сделать, оба эти решения сделают ваши предположения о том, какие типы вы ожидаете, явные.

Если вас не устраивает ни одно из этих решений, и вы можете предоставить более подробную информацию о том, что именно вы пытаетесь сделать, мы могли бы найти что-то еще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...