Swift 5:使用 <T:BinaryInteger> 和 <T:BinaryFloatingPoint> 合并两个泛型函数并指定输出类型

问题描述

我想做什么

我的最终目标是让诸如 [Int][UInt][Float] 之类的数组数据通过一个通用函数来转换任何这些类型并输出 [UInt8] 0 -255 像素数据值。这样我就可以创建一个指向这个 UInt8 数据的指针,用这个指针创建一个 CFData(内置类型),然后最后把 CFData一个 {{1}并将其作为输入以从 CGDataProvider 生成灰度图像。 (我认为这个过程可能更简单,但我就是这样想出来的。)

我的阅读功能如下

CGImage.init()

然而,BinaryFloatingPoint (BF)

也有一个完全相同的函数

mutating func fromfileBI<T : BinaryInteger>(count : Int = 1) -> [T]? { let byteSize = MemoryLayout<T>.stride var swiftTypeOut : [T] = [] if count > 1 { swiftTypeOut = [T].init(repeating: 0,count: count) for i in 0...count-1 { swiftTypeOut[i] = data.subdata(in: off + i * byteSize..<off + (i + 1) * byteSize ).withUnsafeBytes{ $0.load(as: T.self )} } off += count * byteSize } else if count == 1 { swiftTypeOut = [ data.subdata(in: off..<(off+1) * byteSize ).withUnsafeBytes{ $0.load(as: T.self )} ] off += byteSize } else if count == 0 { return [] } else if count < 0 { print("Warning,fRead.int8Read( count : Int = 1) called with a negative count,returning empty array.") return [] } return swiftTypeOut }

以及格式化图像数据的功能(这最后一步是我关心的......所有数据都将成为图像而无需共享数据的原始类型(数据将是[UInt8]从这个函数返回)))

这是 BinaryInteger 符合类型的类型。:

mutating func fromfileBF<T : BinaryFloatingPoint>(count : Int = 1) -> [T]?

} 我想另一个 func formatArrayDataforImage<T>(dataSet : Array< T >,count: Int,numpyTypeName : String = "") -> [UInt8]! where T: BinaryInteger { var f32Data = [Float](repeating: 0,count: count) let obj = f32Data as NSObject // Object to synchronise var UInt8BytesOut = [UInt8](repeating: 0,count: count) dispatchQueue.concurrentPerform(iterations: count){ index in synchronized(obj) { let dataSetValue = dataSet[index] f32Data[index] = Float( dataSetValue ) } } // This dispatchQueue thing was necessary when I was dealing with pointers as input,Now I dont kNow. guard let max = f32Data.max() else { fatalError("Find max fail.")} guard let min = f32Data.min() else { fatalError("find min fail.")} for i in 0..<f32Data.count { f32Data[i] -= min f32Data[i] = 255 * f32Data[i] / max UInt8BytesOut[i] = UInt8( f32Data[i] ) if i<100 { print(UInt8BytesOut[i]) } } return UInt8BytesOut 只会在 formatArrayDataforImage<T: BinaryFloatingPoint> 的地方有所不同,这也应该起作用,我认为将 Float(dataSetValue) 强制转换为 Float32Float 没问题等等。但是函数调用需要 Float8 一致性,所以我们会看到。

RAMBLE:(跳过它只是为了上下文)–––––––

我修复了一个函数,它读取字节并将它们输出为所需的类型,我一直硬编码以将一个数据集加载为 [UInt16] 类型,这有效。但是我从一开始就知道我必须学习如何使类型的处理完全自动化。 (项目代码有点尴尬……我知道我必须打开从源文件读取的字符串值以确定 Swift 数据类型一次……但我最终在阅读时经常需要这个开关,现在我可能不得不再次对开关进行硬编码.. 我如何让调用它的函数知道 Swift 数据类型?我不想使用单独的情况,比如打开数据类型然后转换 BinaryInteger 我怀疑我的问题的答案被埋在这个答案的某个地方——C# generics: cast generic type to value type 但是考虑到我想要指定我的泛型数组来创建指针,我不确定这会如何改变事情。

–––––––––.

结束 希望能一劳永逸地学习如何正确处理 Swift 类型。

解决方法

如果我理解正确,您只是希望 formatArrayDataforImage 接受任何 BinaryInteger 或 BinaryFloatingPoint 数组,而无需重写整个内容。

要做到这一点,请将其编写为浮点数,然后从整数版本中调用它。

我相信这是您的浮点版本:

func formatArrayDataforImage<Element>(dataSet: [Element]) -> [UInt8]
where Element: BinaryFloatingPoint {
    guard let max = dataSet.max() else { fatalError("Find max fail.")}
    guard let min = dataSet.min() else { fatalError("find min fail.")}

    // Note this is a little dangerous since it it crash if
    // min and max aren't in the range 0 - 1. I'd probably add
    // assertions at least.
    return dataSet.map { UInt8(255 * ($0 - min) / max) }
}

没有特别的理由将其转换为 Float。它适用于任何 BinaryFloatingPoint。

然后要为 BinaryInteger 调用它,您只需要将值映射到浮点类型(如 Float),就像您目前正在做的那样。

func formatArrayDataforImage<Element>(dataSet: [Element]) -> [UInt8]
where Element: BinaryInteger  // <=== Note different `where` clause
{
    // Since this creates a [Float] it will call the other function
    formatArrayDataforImage(dataSet: dataSet.map(Float.init))
}

请注意,您的 concurrentPerform 正在尝试并行执行非法操作(您不能同时修改多个线程上的数组,即使您正在修改不同的索引也不行)。事实证明这无关紧要,因为您的同步只是使 concurrentPerform 再次串行。所有这些代码都相当于我的 dataSet.map(Float.init)