How would you extend nested unconstrained Generics in Swift? - ios

How do you extend an array of Measurements without constraining it to a certain UnitType?
import Foundation
extension Array where Element == Measurement { // ERROR: Reference to generic type 'Measurement' requires arguments in <...>
var sum: Double? {
// `UnitType` doesn’t matter here
reduce(0, { $0 + $1.value })
}
}
let array = [
Measurement(value: 1, unit: UnitVolume.bushels),
Measurement(value: 2, unit: UnitLength.fathoms),
]
array.sum // expecting `3`
[Updated with example]

You can create a protocol, make Measurement conform to it and constrain the collection Element to that protocol:
protocol Valued {
var value: Double { get }
}
extension Measurement: Valued { }
extension Collection where Element: Valued {
func test() {
print(self)
}
// note that it is pointless to sum different types of units
var sum: Double {
reduce(0) { $0 + $1.value }
}
// you would need to cast every element to the appropriate unit type before calculating its sum
var totalLengthInMeters: Double {
reduce(0) {
$0 + (($1 as? Measurement<UnitLength>)?.converted(to: .meters).value ?? 0)
}
}
}
let value: Double = 10
let meters: Measurement<UnitLength> = .init(value: value, unit: .meters)
let feet = meters.converted(to: .feet)
let measurements = [meters,feet]
measurements.test() // [{value 10, unit "m"}, {value 32.80839895013123, unit "ft"}]
// note that it is pointless to sum different types of units
measurements.sum // 42.80839895013123
// you would need to cast every element to the appropriate unit type before calculating its sum
measurements.totalLengthInMeters // 20

Related

Reading contents from a generic MTLBuffer?

Within my app, I have a MTLBuffer which is being instantiated using a generic type. In one particular case, the buffer will hold values as related to particles in a point cloud, and is defined as such;
struct ParticleUniforms {
simd_float3 position;
simd_float3 color;
float confidence;
};
I am instantiating my MTLBuffer like so;
guard let buffer = device.makeBuffer(length: MemoryLayout<Element>.stride * count, options: options) else {
fatalError("Failed to create MTLBuffer.")
}
Where I am struggling, however, is to understand how to read the contents of the buffer. More-so, I am looking to copy one element of each item in the buffer to an array on the CPU, which I will use at a later time.
Effectively, the buffer holds a collection of ParticleUniforms, and I would like to access the position value of each item, saving that position to a separate array.
All of the examples I've seen here on Stack Overflow seem to show the MTLBuffer as holding a collection of Floats, though I've not seen any that use a generic type.
It seems what you are looking to achieve can only be done with C structures which hold each member in a contiguous block (arrays of C structs are not necessarily contiguous, but MemoryLayout<Type>.stride will account for any potential padding). Swift structure properties may not be contiguous, so the below method for accessing member values would not work in a practical manner. Unfortunately, when working with void* you need to know what the data describes, which isn't particularly suited for Swift generic types. However, I will offer a potential solution.
C file:
#ifndef Test_h
#define Test_h
#include <simd/simd.h>
typedef struct {
vector_float3 testA;
vector_float3 testB;
} CustomC;
#endif /* Test_h */
Swift file (bridging header assumed)
import Metal
// MARK: Convenience
typealias MTLCStructMemberFormat = MTLVertexFormat
#_functionBuilder
struct ArrayLayout { static func buildBlock<T>(_ arr: T...) -> [T] { arr } }
extension MTLCStructMemberFormat {
var stride: Int {
switch self {
case .float2: return MemoryLayout<simd_float2>.stride
case .float3: return MemoryLayout<simd_float3>.stride
default: fatalError("Case unaccounted for")
}
}
}
// MARK: Custom Protocol
protocol CMetalStruct {
/// Returns the type of the `ith` member
static var memoryLayouts: [MTLCStructMemberFormat] { get }
}
// Custom Allocator
class CustomBufferAllocator<Element> where Element: CMetalStruct {
var buffer: MTLBuffer!
var count: Int
init(bytes: UnsafeMutableRawPointer, count: Int, options: MTLResourceOptions = []) {
guard let buffer = device.makeBuffer(bytes: bytes, length: count * MemoryLayout<Element>.stride, options: options) else {
fatalError("Failed to create MTLBuffer.")
}
self.buffer = buffer
self.count = count
}
func readBufferContents<T>(element_position_in_array n: Int, memberID: Int, expectedType type: T.Type = T.self)
-> T {
let pointerAddition = n * MemoryLayout<Element>.stride
let valueToIncrement = Element.memoryLayouts[0..<memberID].reduce(0) { $0 + $1.stride }
return buffer.contents().advanced(by: pointerAddition + valueToIncrement).bindMemory(to: T.self, capacity: 1).pointee
}
func extractMembers<T>(memberID: Int, expectedType type: T.Type = T.self) -> [T] {
var array: [T] = []
for n in 0..<count {
let pointerAddition = n * MemoryLayout<Element>.stride
let valueToIncrement = Element.memoryLayouts[0..<memberID].reduce(0) { $0 + $1.stride }
let contents = buffer.contents().advanced(by: pointerAddition + valueToIncrement).bindMemory(to: T.self, capacity: 1).pointee
array.append(contents)
}
return array
}
}
// Example
// First extend the custom struct to conform to out type
extension CustomC: CMetalStruct {
#ArrayLayout static var memoryLayouts: [MTLCStructMemberFormat] {
MTLCStructMemberFormat.float3
MTLCStructMemberFormat.float3
}
}
let device = MTLCreateSystemDefaultDevice()!
var CTypes = [CustomC(testA: .init(59, 99, 0), testB: .init(102, 111, 52)), CustomC(testA: .init(10, 11, 5), testB: .one), CustomC(testA: .zero, testB: .init(5, 5, 5))]
let allocator = CustomBufferAllocator<CustomC>(bytes: &CTypes, count: 3)
let value = allocator.readBufferContents(element_position_in_array: 1, memberID: 0, expectedType: simd_float3.self)
print(value)
// Prints SIMD3<Float>(10.0, 11.0, 5.0)
let group = allocator.extractMembers(memberID: 1, expectedType: simd_float3.self)
print(group)
// Prints [SIMD3<Float>(102.0, 111.0, 52.0), SIMD3<Float>(1.0, 1.0, 1.0), SIMD3<Float>(5.0, 5.0, 5.0)]
This is similar to a MTLVertexDescriptor, except the memory is accessed manually and not via the [[stage_in]] attribute and the argument table passed to each instance of a vertex of fragment shader. You could even extend the allocator to accept a string parameter with the name of the property and hold some dictionary which maps to member IDs.

Histogram of Array in Swift

I'm trying to write a generic histogram function that operates on an Array, but I'm running into difficulties as Type 'Element' does not conform to protocol 'Hashable'.
extension Array {
func histogram() -> [Array.Element: Int] {
return self.reduce([Array.Element: Int]()) { (acc, key) in
let value = (acc[key] == nil) ? 1 : (acc[key]! + 1)
return acc.dictionaryByUpdatingKey(key: key, value: value)
}
}
}
where dictionaryByUpdatingKey(...) mutates an existing dictionary as follows:
extension Dictionary {
func dictionaryByUpdatingKey(key: Dictionary.Key, value: Dictionary.Value) -> Dictionary {
var mutableSelf = self
let _ = mutableSelf.updateValue(value, forKey: key)
return mutableSelf
}
}
I have tried replacing Array.Element with AnyHashable and then forcing the key as! AnyHashable, but this seems messy and the return type should preferably be of the same type as the Array.Element and not of AnyHashable.
I wish to use the Array extension as follows:
let names = ["Alex", "Alex", "James"]
print(names.histogram()) // ["James": 1, "Alex": 2]
or
let numbers = [2.0, 2.0, 3.0]
print(numbers.histogram()) // [3.0: 1, 2.0: 2]
Add the generic where clause: where Element: Hashable to your extension:
extension Sequence where Element: Hashable {
func histogram() -> [Element: Int] {
return self.reduce([Element: Int]()) { (acc, key) in
let value = acc[key, default: 0] + 1
return acc.dictionaryByUpdatingKey(key: key, value: value)
}
}
}
I also incorporated #MartinR's suggestion of using the new default value for dictionary look ups.
Using reduce(into:_:) you can do this much more simply and efficiently:
extension Sequence where Element: Hashable {
func histogram() -> [Element: Int] {
return self.reduce(into: [:]) { counts, elem in counts[elem, default: 0] += 1 }
}
}
First, you could limit the Element type to only be Hashable.
extension Array where Array.Element:Hashable {
After this, you might get another error because the swift compiler is a little "overstrained". Try to give him a hint:
typealias RT = [Array.Element: Int]
and use it everywhere. So:
extension Array where Array.Element:Hashable {
typealias RT = [Array.Element: Int]
func histogram() -> RT {
return self.reduce(RT()) { (acc, key) in
let value = (acc[key] == nil) ? 1 : (acc[key]! + 1)
return acc.dictionaryByUpdatingKey(key: key, value: value)
}
}
}
finally should work.

How to add numbers in a string array? Swift

i have an array, var hoursPlayed = String
they are in a tableView and are all numbers, how would i add the numbers in that array together to get the average of hours played????? in Swift 2
You could use reduce:
let sum= hoursPlayed.reduce(0.0,combine:{$0+Float($1)!})
Basically you are iterating through the array and accumulating all the values. Since it is an array of strings,for simplicity I've force unwrapped to a Float, but you must check for the optional. The reduce function takes a closure as argument with 2 parameters. The dollar sign means take the first and the second and sum them.
Now you can easily divide to the number of elements in the array to have an avergae.
If you are in objC world it would be nice use key value coding and the #avg operator.
[UPDATE]
As Darko posted out the first version won't compile. The error was converting the first argument to a Float, since reduce takes an initial value and I put it as Float there is no need for further conversion.
let array = ["10.0", "30.0"]
if array.count > 0 {
let average = array.reduce(0.0, combine: {$0 + (Double($1) ?? 0.0)}) / Double(array.count)
print(average) // 20.0
}
$0 does not need to be converted because it is guaranteed that it's always Double. $0 is inferred from the initial value, which is declared as 0.0: Double.
array.count has to be checked to guard against a division thru 0.
I'd use a combination of flatMap to convert the strings to Doubles and reduce to add them up:
let doubles = array.flatMap { Double($0) }
let average = doubles.reduce(0.0, combine:+) / Double(doubles.count)
Using flatMap protects you from entries in array that can't be converted to Double If you know they all convert you can simplify it to:
let average = array.map({ Double($0)! }) / Double(array.count)
One final option is to extend Array with an average function if that seems like something you'll be more generally using, and use it in combination with flatMap and/or map:
protocol ArithmeticType {
static func zero() -> Self
func +(lhs:Self, rhs:Self) -> Self
func -(lhs:Self, rhs:Self) -> Self
func /(lhs:Self, rhs:Self) -> Self
func *(lhs:Self, rhs:Self) -> Self
init(_ number:Int)
}
extension Double : ArithmeticType {
static func zero() -> Double {
return 0.0
}
}
extension Array where Element : ArithmeticType {
func average() -> Element {
return reduce(Element.zero(), combine:+) / Element(count)
}
}
let avg = array.flatMap { Double($0) }.average()
Modified Darko's approach, which take in account if String is convertible to Double, or not. For an empty array it returns 0.0
let array = ["10.0", "31.2", "unknown", ""]
func avg(arr: [String])->Double {
let arr = array.flatMap(Double.init)
var avg = 0.0
if arr.count > 0 {
avg = arr.reduce(0.0, combine: + ) / Double(arr.count)
}
return avg
}
let a = avg(array)
print(a) // 20.6

Count optional properties in Swift

Is it possible to count all properties who are not nil?
For example:
class test {
var val1:Int?
var val2:Int?
var val3:Int?
var val4:Int?
var val5:Int?
}
var test = test()
test.val1 = 1
test.val2 = 2
How to find out that 2 properties are set? I could check for each one with (!= nil) - but is there an easier (and better) way?
You can do this manually, with a convenience method:
func numberOfNonNil() -> Int {
let vals = [val1, val2, val3, val4, val5]
return flatMap { $0 }.count
}
flatMap(_:) takes a closure that takes a single element of the array and returns an optional value (Element -> T?), and returns the result of applying that closure to each element of the array, with nil values ignored.
The only way to make this simpler would be to store your values as an array of optional Ints in the first place:
class Test {
var vals: [Int?]
}
You can then still access each individual value using the array subscript notation (let val2 = vals[1]). You could then just use the second line in the convenience method above (filter then count) to get the number of non-nil values:
let nonNilCount = vals.flatMap { $0 }.count
If your values are of different types, this approach will still work if you cast the array to a type that encompasses all the different types:
class Test {
var val1: Int?
var val2: Double
var val3: String
var val4: MyRandomClass?
func numberOfNonNil() -> Int {
let vals = [val1, val2, val3, val4, val5] as [Any?]
return flatMap { $0 }.count
}
}
This works because all the values can be expressed as the type Any?.
I don't think there's a way to do this, but you can implement your own function like this:
class test {
var val1:Int?
var val2:Int?
var val3:Int?
var val4:Int?
var val5:Int?
func setVarsCount() -> Int {
var setVariablesCount = 0
if val1 != nil {
setVariablesCount++
}
if val2 != nil {
setVariablesCount++
}
if val3 != nil {
setVariablesCount++
}
if val4 != nil {
setVariablesCount++
}
if val5 != nil {
setVariablesCount++
}
return setVariablesCount
}
}
#Stuarts answer is good, but you have to know the properties of the class, and if you add another property to the class, you also have to modify your method. To avoid this problem, you can use reflection, like
Swift 1.2:
func numberOfOptionalProperties() -> Int {
let mirror = reflect(self)
var numberOfOptionalProperties = 0
for index in 0..<mirror.count {
if mirror[index].1.disposition == .Optional {
++numberOfOptionalProperties
}
}
return numberOfOptionalProperties
}
Swift 2.0:
func numberOfOptionalProperties() -> Int {
return Mirror(reflecting: self).children.reduce(0) {
Mirror(reflecting: $1.value).displayStyle == .Optional ? $0 + 1 : $0
}
}

How to unwrap the elements of an Array in Swift? (ie. Array<Int?> as Array<Int>)

Lets say I have an Array of String and I want to map it to an Array of Int
I can use the map function:
var arrayOfStrings: Array = ["0", "a"]
let numbersOptional = arrayOfStrings.map { $0.toInt() }
// numbersOptional = "[Optional(0), nil]"
Numbers is now an Array of Int?, but I want an Array of Int.
I know I can do this:
let numbers = arrayOfStrings.map { $0.toInt() }.filter { $0 != nil }.map { $0! }
// numbers = [0]
But that doesn't seem very swift. Converting from the Array of Int? to Array of Int requires calling both filter and map with pretty much boilerplate stuff. Is there a more swift way to do this?
Update: As of Swift 1.2, there's a built-in flatMap method for arrays, but it doesn't accept Optionals, so the helper below is still useful.
I like using a helper flatMap function for that sort of things, just like Scala's flatMap method on collections (which can consider an Scala Option as a collection of either 0 or 1 element, roughly spoken):
func flatMap<C : CollectionType, T>(source: C, transform: (C.Generator.Element) -> T?) -> [T] {
var buffer = [T]()
for elem in source {
if let mappedElem = transform(elem) {
buffer.append(mappedElem)
}
}
return buffer
}
let a = ["0", "a", "42"]
let b0 = map(a, { $0.toInt() }) // [Int?] - [{Some 0}, nil, {Some 42}]
let b1 = flatMap(a, { $0.toInt() }) // [Int] - [0, 42]
This definition of flatMap is rather a special case for Optional of what a more general flatMap should do:
func flatMap<C : CollectionType, T : CollectionType>(source: C, transform: (C.Generator.Element) -> T) -> [T.Generator.Element] {
var buffer = [T.Generator.Element]()
for elem in source {
buffer.extend(transform(elem))
}
return buffer
}
where we'd get
let b2 = flatMap(a, { [$0, $0, $0] }) // [String] - ["0", "0", "0", "a", "a", "a", "42", "42", "42"]
Using reduce to build the new array might be more idiomatic
func filterInt(a: Array<String>) -> Array<Int> {
return a.reduce(Array<Int>()) {
var a = $0
if let x = $1.toInt() {
a.append(x)
}
return a
}
}
Example
filterInt(["0", "a", "42"]) // [0, 42]
What you would really want is a collect (map + filter) method. Given the specific filter you need to apply, in this case even a flatMap would work (see Jean-Philippe's answer). Too bad both methods are not provided by the swift standard library.
update: Xcode 7.2 • Swift 2.1.1
let arrayOfStrings = ["0", "a", "1"]
let numbersOnly = arrayOfStrings.flatMap { Int($0) }
print(numbersOnly) // [0,1]
There’s no good builtin Swift standard library way to do this. However, Haskell has a function, mapMaybe, that does what you’re looking for (assuming you just want to ditch nil values). Here’s an equivalent in Swift:
func mapSome<S: SequenceType, D: ExtensibleCollectionType>
(source: S, transform: (S.Generator.Element)->D.Generator.Element?)
-> D {
var result = D()
for x in source {
if let y = transform(x) {
result.append(y)
}
}
return result
}
// version that defaults to returning an array if unspecified
func mapSome<S: SequenceType, T>
(source: S, transform: (S.Generator.Element)->T?) -> [T] {
return mapSome(source, transform)
}
let s = ["1","2","elephant"]
mapSome(s) { $0.toInt() } // [1,2]
You can consider using reduce, it's more flexible:
var arrayOfStrings: Array = ["0", "a"]
let numbersOptional = arrayOfStrings.reduce([Int]()) { acc, str in
if let i = str.toInt() {
return acc + [i]
}
return acc
}

Resources