Swift 5:合并两个具有指定输出类型的 <T: BinaryInteger> 和 <T:BinaryFloatingPoint> 的泛型函数
Swift 5: Merging Two Generic Functions with <T: BinaryInteger> & <T:BinaryFloatingPoint> with Output Type Specified
我想从事的工作
我的最终目标是让 [Int]
、[UInt]
、[Float]
等数组数据通过通用函数传递,该函数转换任何这些类型并输出 [UInt8]
像素数据的 0-255 值。这样我就可以创建一个指向此 UInt8
数据的指针,从该指针创建一个 CFData
(内置类型),然后最后将 CFData
赋给 CGDataProvider
并将其作为输入以从 CGImage.init()
生成灰度图像。 (我认为这个过程可以更简单,但这是我想出来的。)
我的阅读功能如下所示:
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{ [=13=].load(as: T.self )}
}
off += count * byteSize
}
else if count == 1 {
swiftTypeOut = [ data.subdata(in: off..<(off+1) * byteSize ).withUnsafeBytes{ [=13=].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
}
但是 BinaryFloatingPoint (BF)
也有一个相同的主体函数
mutating func fromfileBF<T : BinaryFloatingPoint>(count : Int = 1) -> [T]?
以及格式化图像数据的功能(最后一步是我所关心的...所有数据都将进入图像,而无需共享数据的原始类型(数据将是[ UInt8] 从此函数返回)).
这是符合 BinaryInteger 的类型。:
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)
也应该起作用,我认为将 Float32
转换为 Float
和 Float8
没问题等。但是函数调用要求 BinaryInteger
一致性,所以我们拭目以待。
RAMBLE:(跳过它只是为了上下文)–––––––
我已经修复了一个读取字节并将其输出为所需类型的函数,我一直硬编码以将一个数据集加载为 [UInt16] 类型,这很有效。但我从一开始就知道我必须学习如何使类型处理完全自动化。 (项目代码有点尴尬......我知道我必须打开从源文件读取的字符串值以确定 Swift 数据类型一次..但我最终在阅读时经常需要这个开关,现在我可能不得不再次对开关进行硬编码..我如何才能获得调用它的函数已知的 Swift 数据类型?我不想使用单独的情况,例如打开数据类型然后转换 var example_variable = dataWhosTypeIKnowOnly_FromRawString as [Int32] // <--I know your type from hardcoding the switch
我怀疑我的问题的答案被埋在这个答案的某个地方–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 * ([=10=] - 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)
.
我想从事的工作
我的最终目标是让 [Int]
、[UInt]
、[Float]
等数组数据通过通用函数传递,该函数转换任何这些类型并输出 [UInt8]
像素数据的 0-255 值。这样我就可以创建一个指向此 UInt8
数据的指针,从该指针创建一个 CFData
(内置类型),然后最后将 CFData
赋给 CGDataProvider
并将其作为输入以从 CGImage.init()
生成灰度图像。 (我认为这个过程可以更简单,但这是我想出来的。)
我的阅读功能如下所示:
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{ [=13=].load(as: T.self )}
}
off += count * byteSize
}
else if count == 1 {
swiftTypeOut = [ data.subdata(in: off..<(off+1) * byteSize ).withUnsafeBytes{ [=13=].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
}
但是 BinaryFloatingPoint (BF)
也有一个相同的主体函数mutating func fromfileBF<T : BinaryFloatingPoint>(count : Int = 1) -> [T]?
以及格式化图像数据的功能(最后一步是我所关心的...所有数据都将进入图像,而无需共享数据的原始类型(数据将是[ UInt8] 从此函数返回)).
这是符合 BinaryInteger 的类型。:
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)
也应该起作用,我认为将 Float32
转换为 Float
和 Float8
没问题等。但是函数调用要求 BinaryInteger
一致性,所以我们拭目以待。
RAMBLE:(跳过它只是为了上下文)–––––––
我已经修复了一个读取字节并将其输出为所需类型的函数,我一直硬编码以将一个数据集加载为 [UInt16] 类型,这很有效。但我从一开始就知道我必须学习如何使类型处理完全自动化。 (项目代码有点尴尬......我知道我必须打开从源文件读取的字符串值以确定 Swift 数据类型一次..但我最终在阅读时经常需要这个开关,现在我可能不得不再次对开关进行硬编码..我如何才能获得调用它的函数已知的 Swift 数据类型?我不想使用单独的情况,例如打开数据类型然后转换 var example_variable = dataWhosTypeIKnowOnly_FromRawString as [Int32] // <--I know your type from hardcoding the switch
我怀疑我的问题的答案被埋在这个答案的某个地方–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 * ([=10=] - 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)
.