This is an example from Eloquent Javascript:
By starting from the number 1 and repeatedly either adding 5 or multiplying by 3, an infinite amount of new numbers can be produced. How would you write a function that, given a number, tries to find a sequence of additions and multiplications that produce that number?
I'm having trouble understanding how the recursion is working here, wondering if someone could write out a couple times how find is getting called or some other explanation.
function findSequence(goal) {
function find(start, history) {
if (start == goal)
return history;
else if (start > goal)
return null;
else
return find(start + 5, "(" + history + " + 5)") ||
find(start * 3, "(" + history + " * 3)");
}
return find(1, "1");
}
console.log(findSequence(24)); // => (((1 * 3) + 5) * 3)
Lets leave the history parameter, we'll get to it later.
The recursion expands to all possible operations. It starts with the value
1
asstart
.We first check if we reached our destination:
goal
, if we did- returntrue
, meaning the path we took is correct.Second, we ask- did we go over the bound (
goal
)? If we did, we should returnfalse
since this path can't help us.Otherwise, lets try our two possiblities (We use OR because we need at least one):
start
tostart + 5
start
tostart * 3
The history variable keeps the steps we take. So if a function call identifies that
start == goal
it returns it.goal
is your objective and it's set to 24Now we have this internal function
find()
that checks to see ifstart
is equal to 24; its not. It also checks to see if start is greater then 24 this is also not true,So it hits the else statement where it calls find again, this is where the null value from the
else if()
comes in. If the return is null then it calls the || part until it eventually finds the correct answer.so it switches to the ||
Finally! We have a true return and we have logged the path we took in the history var.
Simply speaking,
find(start,goal)
will be called recursively as long as thegoal
value has not been reached. In each call, the current number will be either multuplied by 3 or incremented by 5. Thehistory
variable stores the string with the operations performed. The current operation is appended to that string in every iteration.Explanation:
You just have to create the tree of invocations to figure this out:
Think of the infinite combinations of adding 5 and multiplying by 3 like a binary tree. At the top is the easiest number to calculate,
1
(in effect a "no steps necessary" answer). Down one level and to the left is1+5
, and to the right is1*3
. At each level the equation resolves to a new value (with a more complex history). This equation is navigating through that tree until it finds a node that equates to thegoal
. If a node on a branch of the tree produces a value greater than your goal, then it returns null (thus halting the further recusing down that branch, this is because both operations only increase the value so once you end up greater than there is no point to keep looking), if the value of a node equates to the goal then it is returned as the answer (along with the path it used to get there). When the value is less than then both paths could potentially hold the answer so it calls find on each. Here is where JavaScript's "truthy" Boolean logic comes in. By using the||
(OR) operator JavaScript will first look down the+5
side of the tree. if 0 or null are returned then the other call (to look down the*3
) will execute. If any return evaluates to a nonfalse
value then it will get returned up the stack and the search will end.