How can you create a generic struct for Numeric numbers that can calculate a percentage?

ε祈祈猫儿з 提交于 2021-02-11 16:59:22

问题


This doesn't compile because Initializer 'init(_:)' requires that 'Number' conform to 'BinaryInteger'

struct Percentage<Number: Numeric> {
    let value: Number
    let total: Number
    var percentage: Double {
        Double(value) / Double(total)
    }
}

Does anyone have a nice solution?

To give some context to the problem from real life: I'm coding a SwiftUI app, that has a CircularProgress-view. I would like to use the same CircularProgress-view with different number types and to be able to show the current value in proportion to min and max. To do that, I need to solve the problem above.


回答1:


The main issue is that Numeric doesn't support generic divisions. One possible solution is to provide multiple generic methods to support different protocols (Integer/FloatingPoint) and Decimal as well:

extension Decimal {
    var number: NSDecimalNumber { self as NSDecimalNumber }
    var double: Double { number.doubleValue }
}

struct Percentage<T: Numeric> {
    let value: T
    let total: T
    func percentage<F: BinaryFloatingPoint>() -> F where T: BinaryFloatingPoint {
        F(value) / F(total)
    }
    func percentage<F: BinaryFloatingPoint>() -> F where T: BinaryInteger {
        F(value) / F(total)
    }
    func percentage<F: BinaryFloatingPoint>() -> F where T == Decimal {
        F(value.double) / F(total.double)
    }
    func percentage() -> Decimal where T == Decimal {
        value / total
    }
}

let percentageInt = Percentage<Int>(value: 10, total: 100)
let result1: Double = percentageInt.percentage()   // 0.1

let percentageDouble = Percentage<Double>(value: 10, total: 100)
let result2: Double = percentageDouble.percentage()   // 0.1
let result3: CGFloat = percentageDouble.percentage()   // 0.1

let percentageDecimal = Percentage<Decimal>(value: 10, total: 100)
let result4 = percentageDecimal.percentage()     // 0.1 decimal
let result5: Double = percentageDecimal.percentage()   // 0.1



回答2:


You can create extensions on Percentage where you restrict Number to BinaryInteger and FloatingPoint to be able to use the / operator.

struct Percentage<Number: Numeric> {
    let value: Number
    let total: Number
}

extension Percentage where Number: BinaryInteger {
    var percentage: Double {
        Double(value) / Double(total)
    }
}

extension Percentage where Number: FloatingPoint {
    var percentage: Number {
        value / total
    }
}

Percentage(value: 15, total: 60).percentage // 25.0
Percentage(value: 1.5, total: 3.0).percentage // 50.0


来源:https://stackoverflow.com/questions/65737343/how-can-you-create-a-generic-struct-for-numeric-numbers-that-can-calculate-a-per

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!