UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Need some help with Checkpoint 4

Forums > 100 Days of SwiftUI

Hi everybody!

I am currently on Checkpoint 4 and there are some things I don't understand. I wrote a function that's supposed to return a square root of a given number, as well as handle errors that may occur. Here's my code:

enum SquareRootErrors: Error {
    case outOfBounds, noRoot
}

func findSquareRoot (for myNumber: Int) throws -> Int {
    for i in 1...10_000 {
        if myNumber < 1 || myNumber > 10_000 {
            throw SquareRootErrors.outOfBounds
        } else if myNumber == 0 {
            throw SquareRootErrors.noRoot
        } else if myNumber == i * i {
            print ("The square root of \(myNumber) is \(i)")
        }
    }
return myNumber

}

let myNumber = 64
do {
  let result = try findSquareRoot(for: myNumber)
    print(result)
} catch SquareRootErrors.outOfBounds {
    print("The number you provided isn't within the accepted range")
} catch SquareRootErrors.noRoot {
    print("The number of your choice has no square root")
} catch {
    print("Something went wrong.")
}

The code sort of works. However, apart from printing the result, it also prints the number alone. It's understandable, since I wrote return myNumber, but I have no idea how to get rid of that part, so that only the square root is printed (as the function should return an integer). When I delete the return part, it gives me a warning.

Also, is myNumber = 0 the only case when there is no root or am I missing something?

I know these are probably very trivial things to ask, but I just didn't want to take an easy route and look for the whole solution. Thanks in advance!

1      

Stop coding for 10 minutes.

Now try to find the square root of 9. Get out a piece of paper and write down the numbers from 1 to 12.

Follow your logic for 1. Write down every decision your code makes.

Follow your logic for 2. Write down every decision your code makes.

Continue this paper trail for the first 12 numbers.

Do you see any repetition? Do you see unnecessary decisions being made?

Here's one... why are you attempting 11 * 11? This number is WAAAAAAY greater than 9! In this example you stopped at 12. But your code takes this all the way up to 10_000.

What other problems did you uncover while running your square root solution on paper?

Coding, whether in Swift, JAVA, C++, Basic, Fortran, Visual Basic, is a lot of puzzle solving. It's not your Swift that's the problem right now. It's the logic you're using to solve the puzzle. How would you describe this solution to a young student?

We could just tell you the answer! But you should probably try to solve this one on your own. First, solve the puzzle. Then figure out the Swift loops, variables, returns, and print statements.

We've all been in your shoes. We're here to answer more questions, but give this another try first.

3      

@grabapprehensive

Well done you nearly there but this ( } else if myNumber == 0 { ) will never be called as if myNumber is zero it will be caught by the outOfBounds error.

The only another thing is you check the number is in the range everytime you go though the loop, so you might want to think about a guard to bail out if the number is out side the range.

@Obelix is right that the loop alway goes to 10_000 when noRoot found. So putting the limit of input number stops this happening

The last thing is you return the orginal number!

here your code amended slightly. hope this is clear.

func findSquareRoot (for myNumber: Int) throws -> Int {
    // bail out if number is not  between the range
    guard myNumber >= 1 && myNumber <= 10000 else {
        throw SquareRootErrors.outOfBounds
    }

    // change the end range to input number to stop loop doing extra work
    for i in 1...myNumber {
        if myNumber == i * i {
            // return the root number of i
            return i
        }
    }

    // If you get here no root number found
    throw SquareRootErrors.noRoot
}

let myNumber = 101

do {
  let result = try findSquareRoot(for: myNumber)
    print ("The square root of \(myNumber) is \(result)")
} catch SquareRootErrors.outOfBounds {
    print("The number you provided isn't within the accepted range")
} catch SquareRootErrors.noRoot {
    print("The number of your choice has no square root")
} catch {
    print("Something went wrong.")
}

2      

You can limit the number of times you go through the central loop even more:

for i in 1...myNumber {
    //if i * i > myNumber, we know we will never hit a match
    //  so we can bail early
    let i2 = i * i
    guard i2 < myNumber else {
        break
    }

    if myNumber == i2 {
        return i
    }
}

Without a guard like this, your loop would run 101 times for input 101. With it, it will only run 11 times (because 10 * 10 == 100 and 11 * 11 == 121, so we know it's impossible to find an answer if we keep going higher).

2      

Thank you so much for your replies. I

@Obelix - thank you for giving me food for thought. I realized that my code ran up to 10_000 every single time, no matter if myNumber would be 1, 4, or 9999. Also, the line:

> else if myNumber == 0 {
            throw SquareRootErrors.noRoot

makes no sense, as 0 is out of the range I'm working with. I do not understand why it took me so long to see it! Thank you again for trying to change not only my code, but my line of reasoning.

As I'm typing this, I noticed that there are another comments from @NigelGee and @roosterboy - now, with your improvements, it all makes sense.

I hope one day I'll be able to help somebody here, just like you did. It's a loooong way to go, but also if you told me 3 weeks ago that I'd be writing pieces of code like this I wouldn't believe you.

Have a great day and thanks for being so supportive ;)

2      

I'm trying to understand why, when I change:

guard number < 10_001 && number > 1 else {

to:

guard number < 10_001 && number > 0 else {

in my playground, it goes from working as expected to saying "The playground encountered a crash and could not finish executing." "0 is out of range." prints on the console. Xcode 14 beta 2, although I doubt that's relevant.

Here's the rest of the code & thanks in advance for clues! :

import Foundation

enum SquareRootError: Error {
  case outofbounds, cantfind
}

func squareRoot(of number: Int) throws -> Int {

  guard number < 10_001 && number > 0 else {
    throw SquareRootError.outofbounds
  }

  for i in 1...number/2 {
    if i * i == number {
      return i
      }
    }
  throw SquareRootError.cantfind
  }

for i in 0...10_002 {
  do {
    let foundRoot = try squareRoot(of: i)
    print("The square root of \(i) is \(foundRoot).")
  } catch SquareRootError.cantfind {
    print("Can't find Int square root for \(i).")
  }
  catch SquareRootError.outofbounds {
    print("\(i) is out of range.")
  }
}

1      

Because the very first value in your for loop triggers the guard condition in your function.

for i in 0...10_002 {
    ....
}

The for loop starts at 0.

guard number < 10_001 && number > 0 else {
    throw SquareRootError.outofbounds
}

If the guard condition encounters a 0, it throws an outofbounds error.

And then you get a crash here:

for i in 1...number/2 {
    ...
}

because when the squareRoot function is given 1 to process, the for loop looks like this:

for i in 1...0.5 {
    ...
}

and that triggers an error since a range's lower bound must be smaller than its upper bound.

And why are you dividing number by 2 anyway?

1      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.