我正在创建一些实用程序类存储数值(Int,Float,Double 等...)。 它也有最小值和最大值。
我需要将值转换为百分比。
所以我在操场上写了如下课程:
import UIKit
class Series<T: SignedNumeric> {
let minValue: T
let maxValue: T
var datas: [T] = []
let range: T
init(minValue: T, maxValue: T) {
self.minValue = minValue
self.maxValue = maxValue
self.range = (maxValue - minValue)
self.datas = []
}
func percent(value: T) -> Float {
let v: Float = value as! Float
let m: Float = minValue as! Float
let r: Float = range as! Float
return (v - m) / r
}
}
let s = Series<Int>(minValue: -100, maxValue: 100)
s.datas = [20, 0, 40, -100, 100]
Float(s.datas[0] - s.minValue) / Float(s.range)
Float(s.datas[1] - s.minValue) / Float(s.range)
Float(s.datas[2] - s.minValue) / Float(s.range)
Float(s.datas[3] - s.minValue) / Float(s.range)
Float(s.datas[4] - s.minValue) / Float(s.range)
s.percent(value: s.datas[0]) // ERROR !!!
当调用 s.percent(value: s.datas[0]) 时,它崩溃了。
如何编写 percent() 函数?
并非每个SignedNumeric
都可以转换为Float
。回想一下,要成为SignedNumeric
,您需要能够:
- 消极和积极
- 乘以
- 加减
- 具有
Comparable
和Numeric
的量级 - 可以从整数转换
最后 4 个要求都是从Numeric
继承而来的。我们可以轻松地构建自己的类型,可以完成所有这些事情。下面是一个人为的例子:
struct MyNumber: SignedNumeric, Comparable {
private var secret: [String]
private var isNegative: Bool
private var number: Int { secret.count }
static func *= (lhs: inout MyNumber, rhs: MyNumber) {
lhs = lhs * rhs
}
static func * (lhs: MyNumber, rhs: MyNumber) -> MyNumber {
MyNumber(secret: Array(repeating: "", count: lhs.number * rhs.number), isNegative: lhs.isNegative != rhs.isNegative)
}
init(integerLiteral value: Int) {
let int = value
isNegative = int < 0
secret = Array(repeating: "", count: abs(int))
}
static func < (lhs: MyNumber, rhs: MyNumber) -> Bool {
lhs.number < rhs.number
}
init?<T>(exactly source: T) where T : BinaryInteger {
guard let int = Int(exactly: source) else {
return nil
}
self.init(integerLiteral: int)
}
var magnitude: MyNumber {
MyNumber(secret: secret, isNegative: false)
}
static func - (lhs: MyNumber, rhs: MyNumber) -> MyNumber {
if lhs < rhs {
return -(rhs - lhs)
} else {
return MyNumber(secret: Array(repeating: "", count: lhs.number - rhs.number))
}
}
prefix static func - (operand: MyNumber) -> MyNumber {
MyNumber(secret: operand.secret, isNegative: !operand.isNegative)
}
mutating func negate() {
isNegative.toggle()
}
init(secret: [String], isNegative: Bool = false) {
self.secret = secret
self.isNegative = isNegative
}
typealias Magnitude = MyNumber
static func + (lhs: MyNumber, rhs: MyNumber) -> MyNumber {
MyNumber(secret: lhs.secret + rhs.secret)
}
typealias IntegerLiteralType = Int
}
<插入在这里进行转换的人>到底是如何知道,要将MyNumber
转换为Float
,它必须Float(aMyNumber.secret.count)
!?更不用说secret
是私人的。插入在这里进行转换的人>
另一方面,Float.init
确实知道如何将BinaryFloatingPoint
和BinaryInteger
转换为Float
,因为这些协议定义了必要的属性,以便Float.init
可以理解它们是如何表示的。例如,BinaryInteger
s 由words
表示,这是UInt
s 的RandomAccessCollection
,由Int
s 索引。
我建议您仅将percent
方法添加到实际有意义的Series
类型中:
extension Series where T : BinaryInteger {
func percent(value: T) -> Float {
let v: Float = Float(value)
let m: Float = Float(minValue)
let r: Float = Float(range)
return (v - m) / r
}
}
IMO,这对任何T : FloatingPoint
都是有意义的,而不仅仅是T : BinaryFloatingPoint
:
extension Series where T : FloatingPoint {
func percent(value: T) -> T {
let v = value
let m = minValue
let r = range
return (v - m) / r
}
}
由于FloatingPoint
也支持除法,因此您无需转换为Float
!
非常感谢!
我添加了两个扩展:
extension Series where T : BinaryInteger {
func percent(value: T) -> Float {
let v: Float = Float(value)
let m: Float = Float(minValue)
let r: Float = Float(range)
return (v - m) / r
}
}
extension Series where T : FloatingPoint {
func percent(value: T) -> T {
let v = value
let m = minValue
let r = range
return (v - m) / r
}
}
现在我可以将 percent() 与 Int、Float、Int16 一起使用... !