How to make a function work with a sequence of additional values?

How to indicate that a function should work with a sequence of optional values โ€‹โ€‹in Swift? For example, I want to make a function that works for an array of optional values โ€‹โ€‹for sequences.

// Given an array of optional values, return the first one with a value, if any func firstValue<E>(ary: [E?]) -> E? { for e in ary { if let x = e { return x } } return nil } 

What I was hoping would work, but this does not happen, because there is no such thing as OptionalType ):

 func firstValue<C: SequenceType where C.Generator.Element: OptionalType>(seq: C) -> C.Generator.Element { var g = seq.generate() while let e = g.next() { return e } return nil } 
+2
source share
2 answers

Try the following:

 func firstValue<E, S: SequenceType where S.Generator.Element == Optional<E> >(seq: S) -> E? { var g = seq.generate() while let e:Optional<E> = g.next() { if e != nil { return e } } return nil } let a:[Int?] = [nil,nil, 42, nil] println(firstValue(a)) // -> 42 as Int? 

I tested with Xcode Version 6.1.1 (6A2006) and Version 6.2 (6C86e)


Note

Without :Optional<E> in while condition, the compiler will work.

And if we declare such a function, the compiler will collide in some environment.

 func firstValue<S: SequenceType, E where S.Generator.Element == Optional<E> > { // ^^^^^^^^^^^^^^^^^^ replaced E and S 

I think this is a compiler error. See comments below.

+2
source

For sequence elements, two operations are required:

  • Check if the element is nil or not, and
  • Create the nil value of the appropriate type as the default return value if nothing is found.

For # 2, we can use the fact that enum Optional conforms to the NilLiteralConvertible protocol. For # 1, I defined the NilComparable protocol and made enum Optional match it:

 protocol NilComparable { func isNil() -> Bool } extension Optional : NilComparable { func isNil() -> Bool { return self == nil } } 

Now we can define a function for all sequences whose elements match up to NilComparable and NilLiteralConvertible . All option sequences fall into this category:

 func firstValue<C: SequenceType where C.Generator.Element : NilComparable, C.Generator.Element : NilLiteralConvertible >(seq: C) -> C.Generator.Element { var gen = seq.generate() while let elem = gen.next() { if !elem.isNil() { return elem } } return nil // Here NilLiteralConvertible is used. } 

Example:

 let arr = [nil, nil, Optional(1), Optional(2)] // Type is [Optional<Int>] println(firstValue(arr)) // Output: Optional(1) 

Update: function already exists

 func !=<T>(lhs: T?, rhs: _OptionalNilComparisonType) -> Bool 

which compares any optional value with nil , so the above protocol can be simplified to

 protocol NilComparable { func !=(lhs: Self, rhs: _OptionalNilComparisonType) -> Bool } extension Optional : NilComparable { } // Already conforming 

Then we can write if elem != nil { ... } instead of if !elem.isNil() { ... } in the function.

A possible flaw is that _OptionalNilComparisonType is not officially documented.


Note. I tried to declare a function as

 func firstValue<C: SequenceType, E where C.Generator.Element == Optional<E> >(seq: C) -> E? { // ... } 

but this actually caused the compiler to crash. I don't know if this needs to be compiled.

+1
source

Source: https://habr.com/ru/post/1206839/


All Articles