mirror of
https://git.sr.ht/~crc_/retroforth
synced 2024-11-16 19:48:56 +01:00
2e95ec501a
FossilOrigin-Name: 708314f58b4253b273c217a11265725bad4c8d7d7c53c503510440dade50be4d
100 lines
2.6 KiB
Text
100 lines
2.6 KiB
Text
# 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
|
|
```
|