musicman1979
musicman1979

Reputation: 105

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
    }

Upvotes: 0

Views: 240

Answers (2)

musicman1979
musicman1979

Reputation: 105

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

Upvotes: 1

watan
watan

Reputation: 36

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
}

Upvotes: 1

Related Questions