How can I enforce a constrained input on callers of a function? [duplicate] - 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

Swift Scope - Returning if and for results from a function

How do i get the result of of i to return from the function here? I can return it from inside the if { } but then I get an error there’s no global return (for the function). My goal is to write a function that accepts an Integer and returns the square root. My next step is to throw an error if it's not an Int or between 1 and 10,000, but please assume simple numbers for this question.
let userInputInteger = 9
func squareRootCheckpoint4(userInputInteger: Int) -> Int {
for i in 1...100 {
let userInputInteger = i * i
if i == userInputInteger {
break
}
}
return i
}
update:
To be able to return it, you will have to declared a variable outside the for-loop, then based on the logic assign the "i" counter to that outside variable.
func squareRootCheckpoint4(userInputInteger: Int) -> Int {
var result = 0 // this is to keep the result
for i in 1...100 {
let powerOfTwoResult = i * i
if powerOfTwoResult == userInputInteger {
result = i // assign result variable based on the logic
break
}
}
return result // return result
}
The error shown is because if the for-loop finishes without any hit on the if statement, the function will not be able to return any Int. That's why the compiler produces that "no global return" error.
One way to do it if you don't want to return a default value is to throw an error. You could throw an error from a function (https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html). For example for your goal of returning square root of an integer and throw error if the input integer is not between 1 and 10000 (if I understand correctly), you could do something like this
enum InputError: Error {
case invalidInteger
}
func squareRootCheckpoint4(userInputInteger: Int) throws -> Int {
if userInputInteger < 1 || userInputInteger > 10000 {
throw InputError.invalidInteger
}
// calculate square root
return resultOfSquareroot
}
// and to handle the error, you could encapsulate the squareRootCheckpoint4 function in a do-catch statement
do {
let squareRootResult = try squareRootCheckpoint4(userInputInteger: 4)
} catch InputError.invalidInteger {
// handle your error here
}
Alternatively, you could do a validation on the input integer separately before calling the squareRootCheckpoint4 function. For example
func validate(_ input: Int) -> Bool {
if userInputInteger < 1 || userInputInteger > 10000 {
return false
}
return true
}
func squareRootCheckpoint4(userInputInteger: Int) -> Int {
// calculate square root
return resultOfSquareroot
}
var input: Int = 9
if validate(input) {
let squareRootResult = squareRootCheckpoint4(input)
} else {
// handle when input is invalid
}
A couple of things are swapped around, and something (anything) needs to be returned from the function. The framing of the question only allows solutions that don't work if the input is not an integer with a square root.
List of Specifics:
The function argument name userInputInteger should not have been used to instantiate a new object. That's confusing. I did it because I'm learning and thought it was necessary.
Instead, i times i (or i squared) should have been assigned to a new object.
The if statement should be equal to this new object, instead of i.
Functions should return something. When i is returned in the if statement, it's not available anywhere outside the for loop. The print statements in the code example help explain what is available in these different scopes, as well as what's not.
The for loop stops when i is returned. That happens when the guess (i) is equal to the input (userInputInteger).
The for loop will continue through 100 if the input doesn't have a square root.
The function should return the correct number that the if statement was trying to guess, but you have to tell it to (with a return statement) and do it in the right spot. This is the "global return" error. However, because this approach in the question is setup poorly without error handling, only an Integer with a sqare root will return correctly. And in this case, the function's global return doesn't matter; Swift just requires something be returned, regardless of whether or not it's used.
Code Example - Direct Answer:
import Cocoa
let userInputInteger = 25
let doesNotMatter = 0
print("sqrt(\(userInputInteger)) is \(sqrt(25))") //correct answer for reference
func squareRootCheckpoint4(userInputInteger2: Int) -> Int {
var j: Int
for i in 1...100 {
print("Starting Loop \(i)")
j = i * i
if j == userInputInteger2 {
print("i if-loop \(i)")
print("j if-loop \(j)")
return i
}
print("i for-loop \(i)")
print("j for-loop \(j)")
}
//print("i func-end \(i)") //not in scope
//print("j func-end \(j)") //not in scope
//return i //not in scope
//return j //not in scope
print("Nothing prints here")
// but something must be returned here
return doesNotMatter
}
print("Input was \(userInputInteger)")
print("The Square Root of \(userInputInteger) is \(squareRootCheckpoint4(userInputInteger2: userInputInteger))")
Code Example - Improved with Error Handling
import Cocoa
enum ErrorMsg : Error {
case outOfBoundsError, noRootError
}
func checkSquareRoot (Input userInputInteger2: Int) throws -> Int {
if userInputInteger < 1 || userInputInteger > 10_000 {
throw ErrorMsg.outOfBoundsError
}
var j : Int
for i in 1...100 {
print("Starting Loop \(i)")
j = i * i
if j == userInputInteger2 {
print("i if-loop \(i)")
print("j if-loop \(j)")
return i
}
print("i for-loop \(i)")
print("j for-loop \(j)")
}
throw ErrorMsg.noRootError
}
let userInputInteger = 25
print("Input was \(userInputInteger)")
do {
let result = try checkSquareRoot(Input: userInputInteger)
print("The Square Root of \(userInputInteger) is \(result)")
} catch ErrorMsg.outOfBoundsError {
print("out of bounds: the userInputInteger is less than 1 or greater than 10,000")
} catch ErrorMsg.noRootError {
print("no root")
}
print("Swift built-in function for reference: sqrt(\(userInputInteger)) is \(sqrt(25))") //correct answer for reference

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")
}

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

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
}