Given a function parameter of type [Int]; can it be constrained to not be empty? - swift

Please consider the following:
func example(array: [Int]) {
guard array.count > 0 else { return }
// Do something
}
It would be nice to announce the restriction via the function signature. Is it possible to constrain the parameter? Something equivalent to:
func example(array: [Int] where array.count > 0) {
// Do something
}
Going one step further: let's make the array optional:
func example(array: [Int]?) {
guard array == nil || array!.count > 0 else { return }
// Do something
}
func example(array: [Int]? where array.count > 0) {
// Do something
}

No, you can't have a conditional parameter. The best you can do is what you have in the second option with an optional and before you call the function (or posibly in the function) check if it is nil.

Related

Compare three enums in swift [duplicate]

Does anybody know of a shortcut to test whether three numbers are the same? I know this works:
if number1 == number2 && number2 == number3 {
}
But I would like something cleaner, such as;
if number1 == number2 == number3 {
}
It's quite important as I'm comparing a lot of different values.
You could use a set
if Set([number1, number2, number3]).count == 1 {
...
though I'd argue it isn't as transparent as multiple if clauses
You can use the power of tuples and the Transitive Property of Equality.
if (number1, number2) == (number2, number3) {
}
The clause of this IF is true only when number1 is equals to number2 AND number2 is equals to number3. It means that the 3 values must be equals.
You can add them in an array and use sets:
var someSet = NSSet(array: [2,2,2])
if someSet.count == 1 {
print("Same values")
}
Don't know of anything other than a Set, I'd suggest wrapping it in a function to make your intent clear. Something along these lines:
func allItemsEqual<T>(items:[T]) -> Bool {
guard items.count > 1 else { fatalError("Must have at least two objects to check for equality") }
return Set(items).count == 1
}
func allItemsEqual(items:T...) -> Bool {
return equal(items)
}
if allItemsEqual(2,3,2) {
// false
}
if allItemsEqual(2, 2, 2) {
// true
}
Beyond that, maybe you could get fancy with operator overloading?
Try this:
func areEqual<T: NumericType>(numbers: T...) -> Bool {
let num = numbers[0]
for number in numbers {
if number != num {
return false
}
}
return true
}
Where NumericType is defined in this post: What protocol should be adopted by a Type for a generic function to take any number type as an argument in Swift?
This will allow you to use the function for all number types
You just pass any number of numbers like:
//returns true
if areEqual(1, 1, 1) {
print("equal")
}

How can I enforce a constrained input on callers of a function? [duplicate]

Please consider the following:
func example(array: [Int]) {
guard array.count > 0 else { return }
// Do something
}
It would be nice to announce the restriction via the function signature. Is it possible to constrain the parameter? Something equivalent to:
func example(array: [Int] where array.count > 0) {
// Do something
}
Going one step further: let's make the array optional:
func example(array: [Int]?) {
guard array == nil || array!.count > 0 else { return }
// Do something
}
func example(array: [Int]? where array.count > 0) {
// Do something
}
No, you can't have a conditional parameter. The best you can do is what you have in the second option with an optional and before you call the function (or posibly in the function) check if it is nil.

Use AnySequence and anyGenerator in combination

Another question asked, essentially, how to implement a take function which would return the first n elements of a sequence. My answer was:
struct TakeFromSequenceSequence<S:SequenceType> : SequenceType {
var limit : Int
var sequence : S
func generate() -> AnyGenerator<S.Generator.Element> {
var generator = sequence.generate()
var limit = self.limit
return anyGenerator {
guard limit > 0 else {
return nil
}
limit = limit - 1
return generator.next()
}
}
}
extension SequenceType {
func take(count:Int) -> TakeFromSequenceSequence<Self> {
return TakeFromSequenceSequence(limit: count, sequence: self)
}
}
but it seems like I ought to be able to use AnySequence and anyGenerator to do it all inline in my take function:
extension SequenceType {
func take(count:Int) -> AnySequence<Self.Generator.Element> {
// cannot invoke initializer for type 'AnySequence<_>' with an argument list of type '(() -> _)'
return AnySequence({
var generator = self.generate()
var limit = count
// cannot invoke 'anyGenerator' with an argument list of type '(() -> _)'
return anyGenerator({
guard limit > 0 else {
return nil
}
limit = limit - 1
return generator.next()
})
})
}
}
Unfortunately, this yields multiple typing errors, mostly (I think) because type inference is failing.
Anybody have any suggestions on how to get this (using AnySequence and anyGenerator inline) to work?
(The answer is now based on Swift 2.2/Xcode 7.3. A solution for Swift 2.1 can be found in the edit history.)
The type of the closure passed to the AnySequence init method
must be specified explicitly:
extension SequenceType {
func take(count:Int) -> AnySequence<Generator.Element> {
return AnySequence { () -> AnyGenerator<Generator.Element> in
var generator = self.generate()
var limit = count
return AnyGenerator {
guard limit > 0 else {
return nil
}
limit = limit - 1
return generator.next()
}
}
}
}
Note that the (redundant) Self. in Self.Generator.Element is omitted, otherwise it does not compile.
Example:
let sequence = [1,2,3,4,5].take(2)
print(Array(sequence)) // [1, 2]
print(Array(sequence)) // [1, 2]
Alternatively, the method can be defined as
extension SequenceType {
func take(count:Int) -> AnySequence<Generator.Element> {
var generator = self.generate()
var limit = count
return AnySequence {
return AnyGenerator {
guard limit > 0 else {
return nil
}
limit = limit - 1
return generator.next()
}
}
}
}
Now the closure passed to the AnySequence init method is a "single-expression closure" and the type is inferred by the compiler.
But – as David Berry noted – the created sequence then behaves differently, the generate() method cannot be called repeatedly
with identical results:
let sequenceX = [1,2,3,4,5].take(2)
print(Array(sequenceX)) // [1, 2]
print(Array(sequenceX)) // []
This is permitted behavior, as stated in the SequenceType protocol reference:
... It is not correct to assume that a sequence will either be
"consumable" and will resume iteration, or that a sequence is a
collection and will restart iteration from the first element. A
conforming sequence that is not a collection is allowed to produce an
arbitrary sequence of elements from the second generator.
So one can choose among these implementations, dependent on the desired behavior.

Recursion over a Swift Sliceable

I feel that I must be missing something obvious. Decomposing a list into the head and tail and then recursing over the tail is a standard functional programming technique, yet I'm struggling to do this for Sliceable types in Swift.
I have a recursive function that follows this pattern:
func recurseArray(arr: [Int]) -> [Int] {
guard let first = arr.first else {
return []
}
let rest = recurseArray(Array(dropFirst(arr)))
let next = rest.first ?? 0
return [first + next] + rest
}
Obviously the real code does a lot more than add each number to the next.
Note the call to Array(dropFirst(seq)). Converting to an Array is required since dropFirst actually returns an ArraySlice, and an ArraySlice isn't a Sliceable, so I can't pass it to my function.
I'm not sure what sort of optimization the compiler is capable of here, but it seems to me that creating a new array from a SubSlice unnecessarily won't be optimal. Is there a solution to this?
Furthermore, what I'd really like to do is create a version of this function that can take any Sliceable type:
func recurseSeq<T: Sliceable where T.Generator.Element == Int>(list: T) -> [Int] {
guard let first = list.first else {
return []
}
let rest = recurseSeq(dropFirst(list)) // <- Error - cannot invoke with argument type T.SubSlice
let next = rest.first ?? 0
return [first + next] + rest
}
This time I don't have a solution to the fact that I have a SubSlice. How can I achieve my goal?
It turns out that there is a generic solution. You need to add these generic requirements:
<
S : Sliceable where S.SubSlice : Sliceable,
S.SubSlice.Generator.Element == S.Generator.Element,
S.SubSlice.SubSlice == S.SubSlice
>
For the question posted, this gives:
func recurseSeq<
S : Sliceable where S.SubSlice : Sliceable,
S.SubSlice.Generator.Element == Int,
S.SubSlice.SubSlice == S.SubSlice,
S.Generator.Element == Int
>(list: S) -> [Int] {
guard let first = list.first else {
return []
}
let rest = recurseSeq(dropFirst(list))
let next = rest.first ?? 0
return [first + next] + rest
}
Here's a useful generic reduce on any sliceable:
extension Sliceable where
SubSlice : Sliceable,
SubSlice.Generator.Element == Generator.Element,
SubSlice.SubSlice == SubSlice {
func recReduce(combine: (Generator.Element, Generator.Element) -> Generator.Element) -> Generator.Element? {
return self.first.map {
head in
dropFirst(self)
.recReduce(combine)
.map {combine(head, $0)}
?? head
}
}
}
[1, 2, 3].recReduce(+) // 6
I can't take credit for this, the solution was posted on the Apple Development Forums.
It's a shame that the generic requirements are so involved for such a a basic operation - it's hardly intuitive! But I'm glad to have a solution...
Actually ArraySlice is Sliceable, so you can recurse on
ArraySlice<Int>:
func recurseArray(arr: ArraySlice<Int>) -> [Int] {
guard let first = arr.first else {
return []
}
let rest = recurseArray(dropFirst(arr))
let next = rest.first ?? 0
return [first + next] + rest
}
with a wrapper function which is called only once at the top level:
func recurseArray(arr: [Int]) -> [Int] {
return recurseArray(arr[arr.startIndex ..< arr.endIndex])
}
I don't have a solution for your second more general problem.
The API docs for Sliceable state that SubSlice should be
Sliceable itself (which is the case for all known Sliceable
types).
I have therefore the feeling that it should be possible by requesting
that T.SubSlice is itself sliceable with the identical SubSlice
type, however this does not compile:
func recurseSeq<T: Sliceable where T.Generator.Element == Int,
T.SubSlice : Sliceable,
T.SubSlice.SubSlice == T.SubSlice>(list: T.SubSlice) -> [Int] {
guard let first = list.first else {
return []
}
let rest = recurseSeq(dropFirst(list) as T.SubSlice)
// error: cannot invoke 'recurseSeq' with an argument list of type '(T.SubSlice)'
let next = rest.first ?? 0
return [first + next] + rest
}
The compiler accepts that dropFirst(list) can be cast to T.SubSlice,
but refuses to call recurseSeq() on that value, which I do not
understand.
Alternatively, you can recurse on a GeneratorType:
func recurseGen<G: GeneratorType where G.Element == Int>(inout gen: G) -> [Int] {
guard let first = gen.next() else {
return []
}
let rest = recurseGen(&gen)
let next = rest.first ?? 0
return [first + next] + rest
}
with a wrapper that takes a SequenceType:
func recurseSeq<T: SequenceType where T.Generator.Element == Int>(list: T) -> [Int] {
var gen = list.generate()
return recurseGen(&gen)
}
Arrays and array slices all conform to SequenceType, so that should
work in all your cases.
Creating an array in every iteration doesn't seem like a good idea. I don't know if the compiler optimizes it somehow, but you could probably find a different solution.
For example, in this case, you could drop de recursion and use a for loop instead that modifies the array in place.
func recurseArray2(var a: [Int]) -> [Int] {
for var i = a.count-1; i > 0; i-- {
a[i-1] += a[i]
}
return a
}

How to prevent a 'Array index out of range' error?

Is there a way, similar to using if let and/or optionals, to test whether you are about to index an empty buffer in Swift?
Define your own:
extension Array {
func ref (i:Int) -> T? {
return 0 <= i && i < count ? self[i] : nil
}
}
The ref() function returns an optional, so it can be nil, and you can use the if let syntax to access the returned value from ref() when it exists. You would use this as such:
var myA = [10,20,30]
if let val = myA.ref(index) {
// Use 'val' if index is < 3
}
else {
// Do this if the index is too high
}