retroforth/example/linked-list.retro
crc 2e95ec501a update more examples to use new word names
FossilOrigin-Name: 708314f58b4253b273c217a11265725bad4c8d7d7c53c503510440dade50be4d
2020-07-06 02:49:49 +00:00

100 lines
2.6 KiB
Forth

# Linked Lists
A linked list is a linear collection of data elements, where the
order is not given by their physical locations in memory. Each
element (called a node) has a secondary field that points to the
next.
+----+---+ +----+---+
| 11 | ----->| 22 | x |
+----+---+ +----+---+
This implements a basic linked list in RETRO. The words here are
in a `ll` namespace. I'm *not* focusing on short names here; I
expect that anything making extensive use of this will provide a
set of aliases or extensions to make things more readable. E.g.,
Rather than:
#1 ll:node-n #2 ll:node-n ll:append #3 ll:node-n
ll:append #4 ll:node-n ll:append
It's easy to write wrappers that allow for things like:
#1 < #2 > #3 > #4 >
# Code
A node is a simple structure with two fields. The first (which I
am calling `head`) holds a value and the second (`next`) holds a
pointer to the next node in the list. If the `next` field points
to an address of zero it is considered the end of the list.
So:
ll:head (a-a) return the address of the head field for a node
ll:next (a-a) return the address of the next field for a node
ll:node-n (n-a) create a new node with a head of n and a next of 0
ll:node (-a) create a new node with both fields set to 0
~~~
:ll:head ;
:ll:next n:inc ;
:ll:node-n here swap , #0 , ;
:ll:node here #0 , #0 , ;
~~~
Next is a helper word to find the end of a list and one to add a node
to the end of the list.
ll:to-end (a-a) return the address of the last node in the list
ll:append (aa-a) set the next field of a1 to the address of a2,
return a1
~~~
:ll:to-end [ ll:next dup fetch n:zero? ] until n:dec ;
:ll:append swap [ ll:to-end ll:next store ] sip ;
~~~
I have a `for-each` combinator to handle performing an action on
each node in a list. As an example:
:< ll:node-n ;
:> ll:node-n ll:append ;
#1 < #2 > #3 > #5 > #1 > #2 > #3 >
[ ll:head fetch n:put sp ] ll:for-each
The docstring for this is:
ll:for-each (aq-a) run q once for each node in the list, passing
the node address to q each time.
~~~
{{
'Action var
:perform
[ @Action call ] sip ;
:at-end?
ll:next fetch dup n:zero? ;
---reveal---
:ll:for-each
&Action [ !Action [ perform at-end? ] until drop ] v:preserve ;
~~~
# Undocumented
~~~
:ll:length
#0 swap [ [ n:inc ] dip ll:next dup fetch n:zero? ] until drop
n:dec #1 n:max ;
~~~
Tests
```
:< ll:node-n ;
:> ll:node-n ll:append ;
#1 < #2 > #3 > #5 > #1 > #2 > #3 > [ ll:head fetch n:put sp ] ll:for-each
```