Общий способ выполнения математических расширений протокола

Цель

Я хочу расширить базовые типы, такие как 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)