Update 'stack language recursion' article to new math delimiters
Signed-off-by: Danila Fedorin <danila.fedorin@gmail.com>
This commit is contained in:
parent
53ff0c39e4
commit
20d8b18a9b
|
@ -48,22 +48,22 @@ concrete syntax. How about something like:
|
||||||
|
|
||||||
Let's informally define the meanings of each of the described commands:
|
Let's informally define the meanings of each of the described commands:
|
||||||
|
|
||||||
1. \\(\\text{Pop} \\; n\\): Removes the top \\(n\\) elements from the stack.
|
1. \(\text{Pop} \; n\): Removes the top \(n\) elements from the stack.
|
||||||
2. \\(\\text{Slide} \\; n \\): Removes the top \\(n\\) elements __after the first element on the stack__.
|
2. \(\text{Slide} \; n \): Removes the top \(n\) elements __after the first element on the stack__.
|
||||||
The first element is not removed.
|
The first element is not removed.
|
||||||
2. \\(\\text{Offset} \\; n \\): Pushes an element from the stack onto the stack, again. When \\(n=0\\),
|
2. \(\text{Offset} \; n \): Pushes an element from the stack onto the stack, again. When \(n=0\),
|
||||||
the top element is pushed, when \\(n=1\\), the second element is pushed, and so on.
|
the top element is pushed, when \(n=1\), the second element is pushed, and so on.
|
||||||
3. \\(\\text{Eq}\\): Compares two numbers on top of the stack for equality. The numbers are removed,
|
3. \(\text{Eq}\): Compares two numbers on top of the stack for equality. The numbers are removed,
|
||||||
and replaced with a boolean indicating whether or not they are equal.
|
and replaced with a boolean indicating whether or not they are equal.
|
||||||
4. \\(\\text{PushI} \\; i \\): Pushes an integer \\(i\\) onto the stack.
|
4. \(\text{PushI} \; i \): Pushes an integer \(i\) onto the stack.
|
||||||
5. \\(\\text{Add}\\): Adds two numbers on top of the stack. The two numbers are removed,
|
5. \(\text{Add}\): Adds two numbers on top of the stack. The two numbers are removed,
|
||||||
and replaced with their sum.
|
and replaced with their sum.
|
||||||
6. \\(\\text{Mul}\\): Multiplies two numbers on top of the stack. The two numbers are removed,
|
6. \(\text{Mul}\): Multiplies two numbers on top of the stack. The two numbers are removed,
|
||||||
and replaced with their product.
|
and replaced with their product.
|
||||||
7. \\(\\textbf{if}\\)/\\(\\textbf{else}\\): Runs the first list of commands if the boolean "true" is
|
7. \(\textbf{if}\)/\(\textbf{else}\): Runs the first list of commands if the boolean "true" is
|
||||||
on top of the stack, and the second list of commands if the boolean is "false".
|
on top of the stack, and the second list of commands if the boolean is "false".
|
||||||
8. \\(\\textbf{func}\\): pushes a function with the given commands onto the stack.
|
8. \(\textbf{func}\): pushes a function with the given commands onto the stack.
|
||||||
9. \\(\\text{Call}\\): calls the function at the top of the stack. The function is removed,
|
9. \(\text{Call}\): calls the function at the top of the stack. The function is removed,
|
||||||
and its body is then executed.
|
and its body is then executed.
|
||||||
|
|
||||||
Great! Let's now write some dummy programs in our language (and switch to code blocks
|
Great! Let's now write some dummy programs in our language (and switch to code blocks
|
||||||
|
@ -106,7 +106,7 @@ of all the computational chaos. We will adopt calling conventions.
|
||||||
When I say calling convention, I mean that every time we call a function, we do it in a
|
When I say calling convention, I mean that every time we call a function, we do it in a
|
||||||
methodical way. There are many possible such methods, but I propose the following:
|
methodical way. There are many possible such methods, but I propose the following:
|
||||||
|
|
||||||
1. Since \\(\\text{Call}\\) requires that the function you're calling is at the top
|
1. Since \(\text{Call}\) requires that the function you're calling is at the top
|
||||||
of the stack, we stick with that.
|
of the stack, we stick with that.
|
||||||
2. If the function expects arguments, we push them on the stack right before the function. The
|
2. If the function expects arguments, we push them on the stack right before the function. The
|
||||||
first argument of the function should be second from the top of the stack (i.e.,
|
first argument of the function should be second from the top of the stack (i.e.,
|
||||||
|
@ -132,7 +132,7 @@ that it is called correctly, of course -- it will receive an integer
|
||||||
on top of the stack. That may not, and likely will not, be the only thing on the stack.
|
on top of the stack. That may not, and likely will not, be the only thing on the stack.
|
||||||
However, to stick by convention 4, we pretend that the stack is empty, and that
|
However, to stick by convention 4, we pretend that the stack is empty, and that
|
||||||
trying to manipulate it will result in an error. So, we can start by imagining
|
trying to manipulate it will result in an error. So, we can start by imagining
|
||||||
an empty stack, with an integer \\(x\\) on top:
|
an empty stack, with an integer \(x\) on top:
|
||||||
|
|
||||||
{{< stack >}}
|
{{< stack >}}
|
||||||
{{< stack_element >}}{{< /stack_element >}}
|
{{< stack_element >}}{{< /stack_element >}}
|
||||||
|
@ -141,7 +141,7 @@ an empty stack, with an integer \\(x\\) on top:
|
||||||
{{< stack_element >}}\(x\){{< /stack_element >}}
|
{{< stack_element >}}\(x\){{< /stack_element >}}
|
||||||
{{< /stack >}}
|
{{< /stack >}}
|
||||||
|
|
||||||
Then, \\(\\text{PushI} \\; 0\\) will push 0 onto the stack:
|
Then, \(\text{PushI} \; 0\) will push 0 onto the stack:
|
||||||
|
|
||||||
{{< stack >}}
|
{{< stack >}}
|
||||||
{{< stack_element >}}{{< /stack_element >}}
|
{{< stack_element >}}{{< /stack_element >}}
|
||||||
|
@ -150,7 +150,7 @@ Then, \\(\\text{PushI} \\; 0\\) will push 0 onto the stack:
|
||||||
{{< stack_element >}}\(x\){{< /stack_element >}}
|
{{< stack_element >}}\(x\){{< /stack_element >}}
|
||||||
{{< /stack >}}
|
{{< /stack >}}
|
||||||
|
|
||||||
\\(\\text{Slide} \\; 1\\) will then remove the 1 element after the top element: \\(x\\).
|
\(\text{Slide} \; 1\) will then remove the 1 element after the top element: \(x\).
|
||||||
We end up with the following stack:
|
We end up with the following stack:
|
||||||
|
|
||||||
{{< stack >}}
|
{{< stack >}}
|
||||||
|
@ -176,14 +176,14 @@ The function must be on top of the stack, as per the semantics of our language
|
||||||
(and, I suppose, convention 1). Because of this, we have to push it last.
|
(and, I suppose, convention 1). Because of this, we have to push it last.
|
||||||
It only takes one argument, which we push on the stack first (so that it ends up
|
It only takes one argument, which we push on the stack first (so that it ends up
|
||||||
below the function, as per convention 2). When both are pushed, we use
|
below the function, as per convention 2). When both are pushed, we use
|
||||||
\\(\\text{Call}\\) to execute the function, which will proceed as we've seen above.
|
\(\text{Call}\) to execute the function, which will proceed as we've seen above.
|
||||||
|
|
||||||
### Get Ahold of Yourself!
|
### Get Ahold of Yourself!
|
||||||
How should a function call itself? The fact that functions reside on the stack,
|
How should a function call itself? The fact that functions reside on the stack,
|
||||||
and can therefore be manipulated in the same way as any stack elements. This
|
and can therefore be manipulated in the same way as any stack elements. This
|
||||||
opens up an opportunity for us: we can pass the function as an argument
|
opens up an opportunity for us: we can pass the function as an argument
|
||||||
to itself! Then, when it needs to make a recursive call, all it must do
|
to itself! Then, when it needs to make a recursive call, all it must do
|
||||||
is \\(\\text{Offset}\\) itself onto the top of the stack, then \\(\\text{Call}\\),
|
is \(\text{Offset}\) itself onto the top of the stack, then \(\text{Call}\),
|
||||||
and voila!
|
and voila!
|
||||||
|
|
||||||
Talk is great, of course, but talking doesn't give us any examples. Let's
|
Talk is great, of course, but talking doesn't give us any examples. Let's
|
||||||
|
@ -191,15 +191,15 @@ walk through an example of writing a recursive function this way. Let's
|
||||||
try [factorial](https://en.wikipedia.org/wiki/Factorial)!
|
try [factorial](https://en.wikipedia.org/wiki/Factorial)!
|
||||||
|
|
||||||
The "easy" implementation of factorial is split into two cases:
|
The "easy" implementation of factorial is split into two cases:
|
||||||
the base case, when \\(0! = 1\\) is computed, and the recursive case,
|
the base case, when \(0! = 1\) is computed, and the recursive case,
|
||||||
in which we multiply the input number \\(n\\) by the result
|
in which we multiply the input number \(n\) by the result
|
||||||
of computing factorial for \\(n-1\\). Accordingly, we will use
|
of computing factorial for \(n-1\). Accordingly, we will use
|
||||||
the \\(\\textbf{if}\\)/\\(\\text{else}\\) command. We will
|
the \(\textbf{if}\)/\(\text{else}\) command. We will
|
||||||
make our function take two arguments, with the number input
|
make our function take two arguments, with the number input
|
||||||
as the first ("top") argument, and the function itself as
|
as the first ("top") argument, and the function itself as
|
||||||
the second argument. Importantly, we do not want to destroy the input
|
the second argument. Importantly, we do not want to destroy the input
|
||||||
number by running \\(\\text{Eq}\\) directly on it. Instead,
|
number by running \(\text{Eq}\) directly on it. Instead,
|
||||||
we first copy it using \\(\\text{Offset} \\; 0\\), then
|
we first copy it using \(\text{Offset} \; 0\), then
|
||||||
compare it to 0:
|
compare it to 0:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -218,7 +218,7 @@ on the stack:
|
||||||
{{< stack_element >}}factorial{{< /stack_element >}}
|
{{< stack_element >}}factorial{{< /stack_element >}}
|
||||||
{{< /stack >}}
|
{{< /stack >}}
|
||||||
|
|
||||||
Then, \\(\\text{Offset} \\; 0\\) duplicates the first argument
|
Then, \(\text{Offset} \; 0\) duplicates the first argument
|
||||||
(the number):
|
(the number):
|
||||||
|
|
||||||
{{< stack >}}
|
{{< stack >}}
|
||||||
|
@ -237,7 +237,7 @@ Next, 0 is pushed onto the stack:
|
||||||
{{< stack_element >}}factorial{{< /stack_element >}}
|
{{< stack_element >}}factorial{{< /stack_element >}}
|
||||||
{{< /stack >}}
|
{{< /stack >}}
|
||||||
|
|
||||||
Finally, \\(\\text{Eq}\\) performs the equality check:
|
Finally, \(\text{Eq}\) performs the equality check:
|
||||||
|
|
||||||
{{< stack >}}
|
{{< stack >}}
|
||||||
{{< stack_element >}}{{< /stack_element >}}
|
{{< stack_element >}}{{< /stack_element >}}
|
||||||
|
@ -265,7 +265,7 @@ As before, we push the desired answer onto the stack:
|
||||||
{{< stack_element >}}factorial{{< /stack_element >}}
|
{{< stack_element >}}factorial{{< /stack_element >}}
|
||||||
{{< /stack >}}
|
{{< /stack >}}
|
||||||
|
|
||||||
Then, to follow convention 3, we must get rid of the arguments. We do this by using \\(\\text{Slide}\\):
|
Then, to follow convention 3, we must get rid of the arguments. We do this by using \(\text{Slide}\):
|
||||||
|
|
||||||
{{< stack >}}
|
{{< stack >}}
|
||||||
{{< stack_element >}}{{< /stack_element >}}
|
{{< stack_element >}}{{< /stack_element >}}
|
||||||
|
@ -274,7 +274,7 @@ Then, to follow convention 3, we must get rid of the arguments. We do this by us
|
||||||
{{< stack_element >}}1{{< /stack_element >}}
|
{{< stack_element >}}1{{< /stack_element >}}
|
||||||
{{< /stack >}}
|
{{< /stack >}}
|
||||||
|
|
||||||
Great! The \\(\\textbf{if}\\) branch is now done, and we're left with the correct answer on the stack.
|
Great! The \(\textbf{if}\) branch is now done, and we're left with the correct answer on the stack.
|
||||||
Excellent!
|
Excellent!
|
||||||
|
|
||||||
It's the recursive case that's more interesting. To make the recursive call, we must carefully
|
It's the recursive case that's more interesting. To make the recursive call, we must carefully
|
||||||
|
@ -294,7 +294,7 @@ The result is as follows:
|
||||||
{{< stack_element >}}factorial{{< /stack_element >}}
|
{{< stack_element >}}factorial{{< /stack_element >}}
|
||||||
{{< /stack >}}
|
{{< /stack >}}
|
||||||
|
|
||||||
Next, we must compute \\(n-1\\). This is pretty standard stuff:
|
Next, we must compute \(n-1\). This is pretty standard stuff:
|
||||||
|
|
||||||
```
|
```
|
||||||
Offset 1
|
Offset 1
|
||||||
|
@ -303,7 +303,7 @@ Add
|
||||||
```
|
```
|
||||||
|
|
||||||
Why these three instructions? Well, with the function now on the top of the stack, the number argument is somewhat
|
Why these three instructions? Well, with the function now on the top of the stack, the number argument is somewhat
|
||||||
buried, and thus, we need to use \\(\\text{Offset} \\; 1\\) to get to it:
|
buried, and thus, we need to use \(\text{Offset} \; 1\) to get to it:
|
||||||
|
|
||||||
{{< stack >}}
|
{{< stack >}}
|
||||||
{{< stack_element >}}\(n\){{< /stack_element >}}
|
{{< stack_element >}}\(n\){{< /stack_element >}}
|
||||||
|
@ -347,7 +347,7 @@ Call
|
||||||
|
|
||||||
If the function behaves as promised, this will remove the top 3 elements
|
If the function behaves as promised, this will remove the top 3 elements
|
||||||
from the stack. The top element, which is the function itself, will
|
from the stack. The top element, which is the function itself, will
|
||||||
be removed by the \\(\\text{Call}\\) operator. The two next two elements
|
be removed by the \(\text{Call}\) operator. The two next two elements
|
||||||
will be removed from the stack and replaced with the result of the function
|
will be removed from the stack and replaced with the result of the function
|
||||||
as per convention 2. The rest of the stack will remain untouched as
|
as per convention 2. The rest of the stack will remain untouched as
|
||||||
per convention 4. We thus expect the stack to look as follows:
|
per convention 4. We thus expect the stack to look as follows:
|
||||||
|
@ -368,7 +368,7 @@ Mul
|
||||||
Slide 1
|
Slide 1
|
||||||
```
|
```
|
||||||
|
|
||||||
The multiplication leaves us with \\(n(n-1)! = n!\\) on top of the stack,
|
The multiplication leaves us with \(n(n-1)! = n!\) on top of the stack,
|
||||||
and the function argument below it:
|
and the function argument below it:
|
||||||
|
|
||||||
{{< stack >}}
|
{{< stack >}}
|
||||||
|
@ -378,7 +378,7 @@ and the function argument below it:
|
||||||
{{< stack_element >}}factorial{{< /stack_element >}}
|
{{< stack_element >}}factorial{{< /stack_element >}}
|
||||||
{{< /stack >}}
|
{{< /stack >}}
|
||||||
|
|
||||||
We then use \\(\\text{Slide}\\) so that only the factorial is on the
|
We then use \(\text{Slide}\) so that only the factorial is on the
|
||||||
stack, satisfying convention 3:
|
stack, satisfying convention 3:
|
||||||
|
|
||||||
{{< stack >}}
|
{{< stack >}}
|
||||||
|
@ -410,7 +410,7 @@ if {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We can now invoke this function to compute \\(5!\\) as follows:
|
We can now invoke this function to compute \(5!\) as follows:
|
||||||
|
|
||||||
```
|
```
|
||||||
func { ... }
|
func { ... }
|
||||||
|
|
Loading…
Reference in New Issue
Block a user