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:
|
||||
|
||||
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__.
|
||||
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__.
|
||||
The first element is not removed.
|
||||
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.
|
||||
3. \\(\\text{Eq}\\): Compares two numbers on top of the stack for equality. The numbers are removed,
|
||||
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.
|
||||
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.
|
||||
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,
|
||||
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,
|
||||
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.
|
||||
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".
|
||||
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,
|
||||
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,
|
||||
and its body is then executed.
|
||||
|
||||
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
|
||||
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.
|
||||
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.,
|
||||
|
@ -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.
|
||||
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
|
||||
an empty stack, with an integer \\(x\\) on top:
|
||||
an empty stack, with an integer \(x\) on top:
|
||||
|
||||
{{< stack >}}
|
||||
{{< stack_element >}}{{< /stack_element >}}
|
||||
|
@ -141,7 +141,7 @@ an empty stack, with an integer \\(x\\) on top:
|
|||
{{< stack_element >}}\(x\){{< /stack_element >}}
|
||||
{{< /stack >}}
|
||||
|
||||
Then, \\(\\text{PushI} \\; 0\\) will push 0 onto the stack:
|
||||
Then, \(\text{PushI} \; 0\) will push 0 onto the stack:
|
||||
|
||||
{{< stack >}}
|
||||
{{< stack_element >}}{{< /stack_element >}}
|
||||
|
@ -150,7 +150,7 @@ Then, \\(\\text{PushI} \\; 0\\) will push 0 onto the stack:
|
|||
{{< stack_element >}}\(x\){{< /stack_element >}}
|
||||
{{< /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:
|
||||
|
||||
{{< 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.
|
||||
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
|
||||
\\(\\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!
|
||||
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
|
||||
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
|
||||
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!
|
||||
|
||||
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)!
|
||||
|
||||
The "easy" implementation of factorial is split into two cases:
|
||||
the base case, when \\(0! = 1\\) is computed, and the recursive case,
|
||||
in which we multiply the input number \\(n\\) by the result
|
||||
of computing factorial for \\(n-1\\). Accordingly, we will use
|
||||
the \\(\\textbf{if}\\)/\\(\\text{else}\\) command. We will
|
||||
the base case, when \(0! = 1\) is computed, and the recursive case,
|
||||
in which we multiply the input number \(n\) by the result
|
||||
of computing factorial for \(n-1\). Accordingly, we will use
|
||||
the \(\textbf{if}\)/\(\text{else}\) command. We will
|
||||
make our function take two arguments, with the number input
|
||||
as the first ("top") argument, and the function itself as
|
||||
the second argument. Importantly, we do not want to destroy the input
|
||||
number by running \\(\\text{Eq}\\) directly on it. Instead,
|
||||
we first copy it using \\(\\text{Offset} \\; 0\\), then
|
||||
number by running \(\text{Eq}\) directly on it. Instead,
|
||||
we first copy it using \(\text{Offset} \; 0\), then
|
||||
compare it to 0:
|
||||
|
||||
```
|
||||
|
@ -218,7 +218,7 @@ on the stack:
|
|||
{{< stack_element >}}factorial{{< /stack_element >}}
|
||||
{{< /stack >}}
|
||||
|
||||
Then, \\(\\text{Offset} \\; 0\\) duplicates the first argument
|
||||
Then, \(\text{Offset} \; 0\) duplicates the first argument
|
||||
(the number):
|
||||
|
||||
{{< stack >}}
|
||||
|
@ -237,7 +237,7 @@ Next, 0 is pushed onto the stack:
|
|||
{{< stack_element >}}factorial{{< /stack_element >}}
|
||||
{{< /stack >}}
|
||||
|
||||
Finally, \\(\\text{Eq}\\) performs the equality check:
|
||||
Finally, \(\text{Eq}\) performs the equality check:
|
||||
|
||||
{{< stack >}}
|
||||
{{< stack_element >}}{{< /stack_element >}}
|
||||
|
@ -265,7 +265,7 @@ As before, we push the desired answer onto the stack:
|
|||
{{< stack_element >}}factorial{{< /stack_element >}}
|
||||
{{< /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_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 >}}
|
||||
|
||||
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!
|
||||
|
||||
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 >}}
|
||||
|
||||
Next, we must compute \\(n-1\\). This is pretty standard stuff:
|
||||
Next, we must compute \(n-1\). This is pretty standard stuff:
|
||||
|
||||
```
|
||||
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
|
||||
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_element >}}\(n\){{< /stack_element >}}
|
||||
|
@ -347,7 +347,7 @@ Call
|
|||
|
||||
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
|
||||
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
|
||||
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:
|
||||
|
@ -368,7 +368,7 @@ Mul
|
|||
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:
|
||||
|
||||
{{< stack >}}
|
||||
|
@ -378,7 +378,7 @@ and the function argument below it:
|
|||
{{< stack_element >}}factorial{{< /stack_element >}}
|
||||
{{< /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 >}}
|
||||
|
@ -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 { ... }
|
||||
|
|
Loading…
Reference in New Issue
Block a user