Общий способ выполнения математических расширений протокола
Цель
Я хочу расширить базовые типы, такие как Int
, Double
, Float
... с более гибкими свойствами и сделать его презентабельным в диаграмме моего приложения. Например, я сделал диаграмму, которая подходит только для отображения Int
, но не может отображать Float
. Я хочу убедиться, что когда передаю аргументы этому представлению, он будет отображаться правильно.
Решение
Итак, я сделал протокол (для этого примера это было так):
protocol SimplyChartable {
static func max(_ dataSet: [SimplyChartable]) -> SimplyChartable
}
И затем сделайте расширение для некоторых типов:
extension Int: SimplyChartable { }
extension Double: SimplyChartable { }
extension Float: SimplyChartable { }
и т.д.
Проблема
Это будут все числовые типы, и всякий раз, когда я передаю его как числовые типы в func
, мне нужно расширить все расширения следующим образом:
public static func max(_ dataSet: [SimplyChartable]) -> SimplyChartable {
return (dataSet as? [Int])?.max() ?? 0
}
Но для Double
func будет идентичным.
Итак, для min я получаю аналогичную функцию, то же самое для деления, добавления, некоторой другой математики... Есть способ записать ее один раз и повторно использовать для каждого типа, который расширяет этот протокол?
Я узнал, что:
let dataType = type(of: maxValue) /* where `maxValue` is SimplyChartable*/
Вернет оригинальный тип как rawValue. Но вывод метода type(of
есть Metatype
, и я не могу вернуть его из функции, а затем добавить два значения этого типа. Так, например, этот код не будет работать:
let val1 = SimplyChartable(4)
let val2 = SimplyChartable(2)
let sum = val1 + val2
И как заставить его работать, не заканчивая тремя функциями вроде этого:
let val1 = SimplyChartable(4)
let val2 = SimplyChartable(2)
let sum = (val1 as! Int) + (val2 as! Int)
Ответы
Ответ 1
Поскольку они все числовые типы, почему вы не используете Comparable?
extension SimplyChartable {
static func max<T: Comparable>(dataSet: [T]) -> T? {
return dataSet.max()
}
static func min<T: Comparable>(dataSet: [T]) -> T? {
return dataSet.min()
}
}
extension Int: SimplyChartable { }
extension Double: SimplyChartable { }
Double.max([1.2, 1.1, 1.3]) // 1.3
Int.min([12, 11, 13]) // 11
Только мои два цента стоят...
Ответ 2
Это не совсем то, о чем вы просили, поскольку он не позволяет вам вызвать статическую функцию непосредственно из метатипа протокола. Но с тех пор AFAIK в Swift сейчас невозможно, возможно, это будет следующая лучшая вещь?
extension Sequence where Element == SimplyChartable {
func max() -> SimplyChartable {
// put your implementation here
}
}
Затем вы можете вызвать это просто:
let arr: [SimplyChartable] = ...
let theMax = arr.max()
Ответ 3
В вашей ситуации гораздо лучше использовать расширение Array, а не протокол с параметром массива.
Чтобы обрабатывать каждый возможный тип массива i.e [Int]
, [Double]
или [Float]
, создайте перечисление обертки со связанными типами следующим образом:
public enum SimplyChartableType {
case int(Int)
case float(Float)
case double(Double)
func getValue() -> NSNumber {
switch self {
case .int(let int):
return NSNumber(value: int)
case .float(let float):
return NSNumber(value: float)
case .double(let double):
return NSNumber(value: double)
}
}
init(int: Int) {
self = SimplyChartableType.int(int)
}
init(float: Float) {
self = SimplyChartableType.float(float)
}
init(double: Double) {
self = SimplyChartableType.double(double)
}
}
Вы можете расширить Array
следующим образом:
extension Array where Element == SimplyChartableType {
func max() -> SimplyChartableType {
switch self[0] {
case .int(_):
let arr = self.map({ $0.getValue().intValue })
return SimplyChartableType(int: arr.max()!)
case .double(_):
let arr = self.map({ $0.getValue().doubleValue })
return SimplyChartableType(double: arr.max()!)
case .float(_):
let arr = self.map({ $0.getValue().floatValue })
return SimplyChartableType(float: arr.max()!)
}
}
}
Пример использования:
var array = [SimplyChartableType.double(3),SimplyChartableType.double(2),SimplyChartableType.double(4)]
var max = array.max()
И теперь гораздо проще работать с Int
, Double
или Float
вместе с:
extension SimplyChartableType: SimplyChartable {
//insert functions here
static func randomFunction() -> SimplyChartableType {
//perform logic here
}
}
Вышеприведенный фрагмент хорош, если вам нужна другая функциональность, которая работает с типами, отличными от коллекции.
Ответ 4
Это, к сожалению, не отвечает на ваш конкретный вопрос. Возможно, работа вокруг, чтобы использовать свободную функцию и кастинг.
import UIKit
protocol SimplyChartable {
func chartableValue() -> Double
}
extension Int: SimplyChartable {
func chartableValue() -> Double {
return Double(self) ?? 0
}
}
extension Double: SimplyChartable {
func chartableValue() -> Double {
return self
}
}
extension Float: SimplyChartable {
func chartableValue() -> Double {
return Double(self) ?? 0
}
}
func maxOfSimplyChartables(_ dataSet: [SimplyChartable]) -> SimplyChartable {
return dataSet.max(by: { (lhs, rhs) -> Bool in
return lhs.chartableValue() < rhs.chartableValue()
}) ?? 0
}
let chartableItem1: SimplyChartable = 1255555.4
let chartableItem2: SimplyChartable = 24422
let chartableItem3: SimplyChartable = 35555
let simplyChartableValues = [chartableItem1, chartableItem2, chartableItem3]
maxOfSimplyChartables(simplyChartableValues)