I've been working through CodeWars katas and I came across a pretty cool solution that someone came up with. The problem I have is I don't understand how it works. I understand some of it like what it is generally doing but not detail specifics. Is it returning itself? How is it doing the calculation? Can someone explain this to me because I really what to learn how to do this. And if you know of any other resources I can read or watch that would be helpful. I didn't see anything like this in the Swift documentation.
func findDigit(_ num: Int, _ nth: Int) -> Int {
let positive = abs(num)
guard nth > 0 else { return -1 }
guard positive > 0 else { return 0 }
guard nth > 1 else { return positive % 10 }
return findDigit(positive / 10, nth - 1) }
For context:
Description:
The function findDigit takes two numbers as input, num and nth. It outputs the nth digit of num (counting from right to left).
Note
If num is negative, ignore its sign and treat it as a positive value. If nth is not positive, return -1. Keep in mind that 42 = 00042. This means that findDigit(42, 5) would return 0.
Examples
findDigit(5673, 4) returns 5
findDigit(129, 2) returns 2
findDigit(-2825, 3) returns 8
findDigit(-456, 4) returns 0
findDigit(0, 20) returns 0
findDigit(65, 0) returns -1
findDigit(24, -8) returns -1
Greatly appreciate any help. Thanks.
This is a simple recursive function. Recursive means that it calls itself over and over until a condition is satisfied that ends the recursion. If the condition is never satisfied, you'll end up with an infinite recursion which is not a good thing :)
As you already understand the purpose of the function, here are the details of how it works internally:
Let's run through an example step by step:
This is a recursive algorithm. It works by solving the original problem by reducing it to a smaller problem of the same time, then solving that, recursively, until a base case is hit.
I think you'll have a much easier time understanding it if you see the calls being made. Of course, it's best to step through this in the debugger to really see what's going on. I've numbered the sections of interest to refer to them below
I print out the function and its parameters, do you can see what's going on. Here's what's printed:
Take the positive value of
num
, so the-
sign doesn't get in the way.Assert that the
nth
variable is greater than0
. Since the digit counting in this problem, any value equal to less0
is invalid. In such a case,-1
is returned. This is very bad practice in Swift. This is what Optionals exist for. It's much better to make this function returnInt?
and returningnil
to represent an error in thenth
variable.Assert that the
positive
variable is greater than 0. The only other possible case is thatpositive
is 0, in which case its digit (for any position) is0
, so that's why you havereturn 0
.Assert that
nth
is greater than1
. If this is not the case, thennth
must be 1 (the guard numbered#3
ensures it can't be negative, or0
. In such a case, the digit in the first position of a decimal number is that number modulo 10, hence whypositive % 10
is returned.If we reach this line, than we know we have a sane value of nth (
> 0
), which isn't1
, and we have a positive number greater than 0. Now we can proceed to solve this problem by recursing. We'll divid positive by 10, and make it into the newnth
, and we'll decrementnth
, because what is thenth
digit of this call, will be in then-1 th
spot of the next call.Someone by the name of JohanWiltink on CodeWars answered my question. But I chose to accept Nicolas's for the detail.
This was JohanWiltink explanation:
The function does not return itself as a function; it calls itself with different arguments and returns the result of that recursive call (this is possibly nested until, in this case, nth=1).
findDigit(10,2) thus returns the value of findDigit(1,1).
If you're not seeing how this works, try to work out by hand what e.g. findDigit(312,3) would return.
Thanks so much to everyone that answered! Really appreciate it!
It is a recursive solution. It does not return itself, per se, it calls itself on a simpler case, until it gets to a base case (here a 1 digit number). So for example, let us trace through what it does in your first example: findDigit(5673, 4) calls findDigit (567, 3) calls findDigit (56,2) calls findDigit (5,1) which is the base case which returns 5 which bubbles all the way back up to the surface.