TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Checkpoint 4: Help

Forums > 100 Days of SwiftUI

Hi! I'm having some trouble with Checkpoint 4. I know it's simple but I'm getting quite confused. Could anybody evaluate my code and see if I'm on the right track?

Here's my code:


// Write a function that accepts an integer from 1 through 10,000 and
// return the integer square root of that number.
//      - Cannot use the built in sqrt() function.
//      - If the number is less than 1 or greater than 10,000 throw an out of bounds error.
//      - Only consider integer square roots.
//      - If you can't find the square root, throw a "no root" error.

enum sqrtError: Error {
    // can also be written as case outOfBounds, nonInteger, noRoot
    // (all in a single line and just one case keyword).
    case outOfBounds
    case nonInteger
    case noRoot
}

func sqrtSolver (for number: Int) throws -> Int {
    if number < 1 {
        throw sqrtError.outOfBounds
        print ("number should not be less than 1")
    }
    else if number > 10_000 {
        throw sqrtError.outOfBounds
        print("number should not exceed 10,000")
    }
    else if !Int(number) {
        throw sqrtError.nonInteger
        print("number should be an integer")
    }
    else if number != sqrtAns {
        throw sqrtError.noRoot
        print("there's no square root for that number")
    }

    for i in 1 ... 10000 {
        var sqrtAns = i * i
    }
    return sqrtAns
}

Here are also a few issues that I'm encountering:

  1. I'm not sure as to how I could validate if the number is an integer. I was thinking !Int(number) but it seems wrong
  2. Why does it say it can't find sqrtAns in scope even if I already created a var sqrtAns?
  3. I'd also like to clarify if I should always add the type :Error in enums? Especially if it's for error handling?

2      

A new hacker considers their approach:

I'm not sure as to how I could validate if the number is an integer. I was thinking !Int(number) but it seems wrong

This is the power of function signatures!

// Your signature
func sqrtSolver (for number: Int) throws -> Int            // <-- Right here!

Your function's signature defines what it will accept, what it will return, and whether it detects errors for you. Your signature tells any programmers using your function that you will only accept integers. Probably not necessary to check inside your code if, indeed, you received an Integer or a Double.

Try it in Playgrounds!

// Copy into a Playground
var greeting = "Hello, playground"

// Function's signature is pretty clear!
// Only accept integers!
func testInteger(_ someNumber: Int) -> Bool {
    Int(someNumber) == someNumber ? true : false
}

testInteger(42)     ? "Integer" : "Not an integer"
testInteger(3.1415) ? "Integer" : "Not an integer"  // <-- Swift complains about this line. WHY?

2      

New hacker has a missing variable.

Why does it say it can't find sqrtAns in scope even if I already created a var sqrtAns?

Swift only holds onto the variables it needs. If you define a variable inside a scope, Swift will delete the variable once you destroy that scope. For your knowledge bank, you need to recognize where your scope begins and ends.

Again, try this code snip in a Playground

// What's in scope?
// ---------------- NEW SCOPE (function)
func whereIsThePenny() {
    let dime = 10 // <-- Exists in the function's scope
    var foundThePenny = false //  <-- set a boolean in the function scope
    // ------------------------ NEW SCOPE (if)
    if !foundThePenny {
        // This is a NEW SCOPE
        let penny = 1 // <-- only exists in the IF scope.
        print("found the penny. it's \(penny) cent.")
        print("found the dime. it's \(dime) cents.")
    } // <-- The scope of the IF statement ends. No more penny.
    // ------------------------ END OF IF SCOPE

    print("still have the dime. still \(dime) cents.")
    print("where did the penny go? \(penny)")  // <-- Not in scope anymore.
}
// ---------------- END OF Function SCOPE
// No penny, no foundThePenny, no dime.

whereIsThePenny()  // <-- Run this line in Playgrounds

2      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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

I see

testInteger(3.1415) ? "Integer" : "Not an integer"  // <-- Swift complains about this line. WHY?

This is because we've indicated in our function testInteger that someNumber parameter should be of type Int

func testInteger(_ someNumber: Int)

Just to clarify, so that in itself is already a validation right? This means that I no longer need to add it in our enums as it already handles any other inputs that is not an integer.

This took me quite a while but I think this version of my updated code is a bit better (let me know if it indeed is or if I'm just feeling like it is but it is actually not)

// Write a function that accepts an integer from 1 through 10,000 and
// return the integer square root of that number.
//      - Cannot use the built in sqrt() function.
//      - If the number is less than 1 or greater than 10,000 throw an out of bounds error.
//      - Only consider integer square roots.
//      - If you can't find the square root, throw a "no root" error.

enum sqrtError: Error {
    // can also be written as case outOfBounds, nonInteger, noRoot
    // (all in a single line and just one case keyword).
    case outOfBounds
    case nonInteger
    case noRoot
}

func sqrtSolver (for number: Int) throws -> Int {
    var sqrtAns: Int = 0
    if number < 1 {
        print ("number should not be less than 1")
        throw sqrtError.outOfBounds
    }
    else if number > 10_000 {
        print("number should not exceed 10,000")
        throw sqrtError.outOfBounds
    }

    for i in 1 ... 10000 {
        sqrtAns = i * i
        if number != sqrtAns {
            print("there's no square root for that number")
            throw sqrtError.noRoot
        }
        else if number == sqrtAns {
            sqrtAns = i
        }
    }
    return sqrtAns
}

sqrtSolver(for: 25)  // Error stating that "call can throw but is not marked with try" Why? I thought we could just call the function then pass an argument for the parameter?

I think this is still far from the proper answer but any guidance/clues would be really helpful. I feel like it's almost there and I'd definitely love it if I were to be able to figure the answer out on my own. Thanks hacker mentor!

2      

Because your function may throw an error, you need to attempt the function by using a try statement.

let myFavoriteSquare = 25
do {
    // ATTEMPT this function
    try sqrtSolver(for: myFavoriteSquare)
} catch sqrtError.noRoot {
    // CATCH the failures
    print("No root for this? -> \(myFavoriteSquare)")
}

Are you surprised by the answer your code produces?

2      

Hey man. Sorry it took me a while. I tried to think as to why 25 returned no root and I'm feeling a bit stucked here. Shouldn't it display 5 since my current loop states that

    for i in 1 ... 10000 {
        sqrtAns = i * i
        if number != sqrtAns {
            print("there's no square root for that number")
            throw sqrtError.noRoot
        }
        else if number == sqrtAns {
            sqrtAns = i
        }
    }
    return sqrtAns
}

The way I'm thinking is if we're looking for the square root of a number (let's say 25), it'll iterate from 1 to 10,000. If the value of the number that we're looking for matches the answer (25) we'll take i and display it as the square root.

25 = 5 x 5

therefore, since 25 (sqrtAns == number), i (5) would be the square root. I tried interchanging the if and else if statement but I'm not quite sure as to how to proceed any further. Any clues would be greatly appreciated. Thanks again.

2      

Try working through the logic of your loop one number at a time. You should soon see the problem.

2      

    for i in 1 ... 10000 {
        sqrtAns = i * i
        if number != sqrtAns {
            print("there's no square root for that number")
            throw sqrtError.noRoot
        }
        else if number == sqrtAns {
            sqrtAns = i
        }
    }
    return sqrtAns
}

Looping from 1 to 10,000

1 = 1 x 1
4 = 2 x 2
9 = 3 x 3
16 = 4 x 4
25 = 5 x 5
else if number (25) == sqrtAns (25) {
      sqrtAns = i (5)
}

I see. So it immediately throws the sqrtError.noRoot after the first iteration of the loop? Is my understanding correct?

2      

A lightbulb moment!

So it immediately throws the sqrtError.noRoot after the first iteration of the loop? Is my understanding correct?

Exactly! Swift is doing exactly what you've told it to do.

If the math doesn't add up, then you tell it to THROW AN ERROR.

Perhaps you'll want to consider ALL the possibilities before you THROW AN ERROR.

2      

Just to be 100% sure, once it throws an error, it immediately exits the loop right?

Right now, I figured out that I should add an additional condition just to make sure that it doesn't throw an error.

for i in 1 ... 10000 {
        sqrtAns = i * i
        if number != sqrtAns && sqrtAns > number {
            print("there's no square root for that number")
            throw sqrtError.noRoot
        }
    }
    return sqrtAns

This means that if the sqrtAns is already bigger than the user's number (square root of the number that the user is looking for), then there's no way there could be a square root for that number. However, I've been racking my brain and upon running this code, it still returns a "There's no square root for that number" error.

I've now updated my code into

func sqrtSolver (for number: Int) throws -> Int {
    var sqrtAns: Int = 0
    if number < 1 {
        print ("number should not be less than 1")
        throw sqrtError.outOfBounds
    }
    else if number > 10_000 {
        print("number should not exceed 10,000")
        throw sqrtError.outOfBounds
    }

    for i in 1 ... 10000 {
        sqrtAns = i * i
        if number != sqrtAns && sqrtAns > number {
            print("there's no square root for that number")
            throw sqrtError.noRoot
        }
        else if number == sqrtAns {
            return i
        }
    }
    return sqrtAns
}

The logic behind this is that as long as the number is not equal to sqrtAns and that the sqrtAns is not yet larger than the original number that the user is looking for the square root for, we will keep on running the for loop. However, once it matches, the square root would be i.

I tried running this but it doesn't seem to run properly. Am I still missing something here?

enum sqrtError: Error {
    // can also be written as case outOfBounds, nonInteger, noRoot
    // (all in a single line and just one case keyword).
    case outOfBounds
    case nonInteger
    case noRoot
}

func sqrtSolver (for number: Int) throws -> Int {
    var sqrtAns: Int = 0
    if number < 1 {
        print ("number should not be less than 1")
        throw sqrtError.outOfBounds
    }
    else if number > 10_000 {
        print("number should not exceed 10,000")
        throw sqrtError.outOfBounds
    }

    for i in 1 ... 10000 {
        sqrtAns = i * i
        if number != sqrtAns && sqrtAns > number {
            print("there's no square root for that number")
            throw sqrtError.noRoot
        }
        else if number == sqrtAns {
            return i
        }
    }
    return sqrtAns
}

// Because your function may throw an error, you need to attempt the function by using a try statement.
let myFavoriteSquare = 25
do {
    // ATTEMPT this function
    try sqrtSolver(for: myFavoriteSquare)
} catch sqrtError.noRoot {
    // CATCH the failures
    print("No root for this? -> \(myFavoriteSquare)")
}

// Additional notes:
// If your function has the "throws" keyword in it (it could throw an error
// if something goes wrong within the function). You have to ALWAYS call the
// function using a do { try functionName(arguments) } catch { } in order to use it.

2      

Hi! Updating this thread for anyone in the future who'll probably encounter the same error as me. I've finally figured out the solution to this although it took me days to do so (I know it's that bad).

I hope this will also be able to help you especially if you're also experiencing difficulties with this problem. I'll try to explain everything and be as comprehensive as possible as well.

    var sqrtAns = 0

    if number < 1 {
        print("number should not be less than 1")
        throw sqrtError.outOfBounds
    } else if number > 10_000 {
        print("number should not exceed 10,000")
        throw sqrtError.outOfBounds
    }

    // Start the loop to find the integer square root
    // Divide number by 2 to make the loop more efficient
    for i in 1 ... number / 2 {
        // Calculate the square of i
        let square = i * i

        // Check if the square is equal to the number
        // If yes, return the square root
        if square == number {
            return i
        }

        // Check if the square exceeds the number
        // If yes, throw the noRoot error
        if square > number {
            print("there's no square root for that number")
            throw sqrtError.noRoot

    }
        // If the loop completes without finding a perfect square root,
        // We should throw the noRoot error here to handle this case.
        throw sqrtError.noRoot
}

We started by declaring a value for sqrtAns and set it into a default of 0. This will be a place holder for our sqrtAns (Final answer which solves the square root of whatever number we want)

Let's start with the if else conditions here

    if number < 1 {
        print("number should not be less than 1")
        throw sqrtError.outOfBounds
    } else if number > 10_000 {
        print("number should not exceed 10,000")
        throw sqrtError.outOfBounds
    }

We basically created two conditions which checks if the number that we're looking for is within the range of 1 to 10,000 (as per requirements)

The next thing that we'll be doing is to calculate for the value of the square root. Here, we will be using a for loop to iterate over the values.

 // Start the loop to find the integer square root
    // Divide number by 2 to make the loop more efficient
    for i in 1 ... number / 2 {
        // Calculate the square of i
        let square = i * i

We divided the original number by 2 as it would make our loop exit faster (if there's no possible square root). The reason we did this is we wanted to minimize the number of iterations for our loop - which makes sense. If you're looking for the square root of a number, the answer cannot exceed the number itself. Additionally, the square root, cannot be more than half of the number itself as well since

sqrtAns = squared x squared

This means that we can divide the number by two to further reduce the number of iterations that our for loop will have to do.

To calculate for the square root, we initialized another integer named square and assigned it to the value of i x i (current index multiplied to itself)

We will then compare if square matches the value of our number

Here, if the computed value for square is equal to the number that we want to look for the square root for, 
then that means that i would automatically be its square root.

          if square == number {
            return i
        }

However, once the value of square already exceeds the number that we're looking for, then there would be no square root
for that number. Which is why we will be throwing a noRoot error
        if square > number {
            print("there's no square root for that number")
            throw sqrtError.noRoot

    }

I've added one last noRoot error outside our for loop just in case it finishes the loop and wasn't able to enter our if else statement. - This also means that there's no square root as it completed the for loop without even entering our if square == number statement.

I hope my explanation was comprehensive enough and it provided clarity as to how the problem should be solved. Marking the thread as solved now.

2      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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

Reply to this topic…

You need to create an account or log in to reply.

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.