1. Random-access-lists
    1. Documentation
      1. random-access-lists
      2. make-ral
      3. ral->list
      4. ral-add!
      5. ral-add-left!
      6. ral-clear!
      7. ral-count
      8. ral-cursor-jump
      9. ral-cursor-next
      10. ral-eq?
      11. ral-eql?
      12. ral-equal?
      13. ral-eqv?
      14. ral-filter
      15. ral-for-each
      16. ral-from-upto
      17. ral-height
      18. ral-insert!
      19. ral-item?
      20. ral-join
      21. ral-level
      22. ral-map
      23. ral-max-height
      24. ral-node?
      25. ral-null?
      26. ral-place
      27. ral-place-next
      28. ral-print
      29. ral-ref
      30. ral-remove!
      31. ral-restructure
      32. ral-set!
      33. ral-split
      34. ral-start
      35. ral-width
      36. ral?
    2. Examples
  2. Requirements
  3. Last update
  4. Author
  5. License
  6. Version History

Random-access-lists

Random-access-lists combine the advantages of vectors (fast access) and linked-lists (fast insertions). They can be implemented on the basis of skiplists.

Whereas an ordinary skiplist-node consists of an item and a vector of next nodes, whose length is computed randomly in such a way, that the number of nodes with length n are in the average one half of the number of nodes with length (- n 1), a random-access-list-node must have an additional vector of same length containing the jumps, i.e. numbers indicating how far the node is moved when following the next nodes at a given level. Following a node at level n describes a fast lane across the random-access-list, where the jumps at level n are in the average twice as long as the jumps at the level below.

In our implementation of random-access-lists we store a vector of nodes, called cursors, and a vector of positions, called places, which are updated along the movement accross the list. Moving cursors and places to a given position, pos, works as follows: One starts at the highest level and follows the next link at that level adding the jump at that level to the place at that level until the ladder is less than pos but a further movement at that level would be greater or equal pos. Then one saves cursor and place and restarts the same movement process at the level below, starting with the values saved in the level above. Eventually one reaches level 0 and stops at a place one less than pos. The stored cursors can than be used to insert or remove an item, as well as getting or setting pos' item. Note, that in the latter case we only need to step down until a level where the next place is equal to pos. Since this cursor and place movement is O(log n), so are all the fundamental random-access-list operations, insert!, remove!, ref and set!

The other supplied operators like map, filter split and join work only at a fixed level, whence are ordinary linked list operators, which perform as O(n).

Some additional remarks are in order.

We described the process with a width of two, i.e. increasing the level of movement doubles the jumps of next nodes in the average. A higher value than two for the width is possible as well, trading performance against space.

We said nothing about the maximal length of the nodes, i.e. of the maximal height of the random-access-list. Our default is 10, but this can be changed in the constructor. This should be appropriate in most cases. But note, that the highest actual, i.e. computed, node height might be smaller, so it must be updated in the list, so that the cursor knows where to start.

Documentation

In this implementation random-access-lists are implemented in the Design by Contract style, i.e. using the dbc module. A corollary of this is, that the documentation is included in one of the two modules in form of a procedure with the module's name. Apart from this documentation procedure the two modules, %random-access-lists and random-access-lists, have the same interface. The first module contains the raw implementations of the procedures, the second imports the first with prefix % and wraps those prefixed routines with contracts.

random-access-lists

[procedure] (random-access-lists [symbol|string])

returns all available routines of the module when called without an argument. When called with one of these routines as a symbol, returns its contract. When called with a string, writes a file with name of string containing rudimentary wiki documentation.

make-ral

[procedure] (make-ral item? . args)

function (result)


(_ item? . args)
requires (and ((list-of? (lambda (arg) (and (fixnum? arg) (fx> arg 1)))) args)
              (procedure? item?) "(item? item)")
ensures  (%ral? result)

ral->list

[procedure] (or (ral->list ls) (ral->list ls level))

function (result)


(_ ls)
requires (%ral? ls)
ensures  ((list-of? (%ral-item? ls)) result)

(_ ls level)
requires (and (%ral? ls) (fixnum? level)
              (fx<= 0 level) (fx< level (%ral-height ls)))
ensures  ((list-of? (%ral-item? ls)) result)

ral-add!

[procedure] (ral-add! ls item . items)

command ((oldcount newcount (lambda (ls item . items) (%ral-count ls))))


(_ ls item . items)
requires (and (%ral? ls) ((%ral-item? ls) item)
              ((list-of? (%ral-item? ls)) items))
ensures  (fx= newcount (fx+ (length (cons item items)) oldcount))

ral-add-left!

[procedure] (ral-add-left! ls item . items)

command ((oldcount newcount (lambda (ls item . items) (%ral-count ls))))


(_ ls item . items)
requires (and (%ral? ls) ((%ral-item? ls) item)
              ((list-of? (%ral-item? ls)) items))
ensures  (fx= newcount (fx+ (length (cons item items)) oldcount))

ral-clear!

[procedure] (ral-clear! ls)

command ((oldcount newcount %ral-count) (oldheight newheight %ral-height))


(_ ls)
requires (%ral? ls)
ensures  (and (fx= 0 newcount) (fx= 1 newheight))

ral-count

[procedure] (ral-count ls)

function (result)


(_ ls)
requires (%ral? ls)
ensures  (and (fixnum? result) (fx>= result 0))

ral-cursor-jump

[procedure] (ral-cursor-jump ls k)

function (result)


(_ ls k)
requires (and (%ral? ls) (fixnum? k)
              (fx>= k 0) (fx< k (%ral-height ls)))
ensures  (and (fixnum? result)
              (fx> result 0) (fx<= result (%ral-count ls)))

ral-cursor-next

[procedure] (ral-cursor-next ls k)

function (result)


(_ ls k)
requires (and (%ral? ls) (fixnum? k)
              (fx>= k 0) (fx< k (%ral-height ls)))
ensures  (or (null? result) (%ral-node? result))

ral-eq?

[procedure] (ral-eq? ls0 ls1)

function (result)


(_ ls0 ls1)
requires (and (%ral? ls0) (%ral? ls1))
ensures  (boolean? result)

ral-eql?

[procedure] (ral-eql? eql? ls0 ls1)

function (result)


(_ eql? ls0 ls1)
requires (and (procedure? eql?) "(eql? item0 item1)"
              (%ral? ls0) (%ral? ls1))
ensures  (boolean? result)

ral-equal?

[procedure] (ral-equal? ls0 ls1)

function (result)


(_ ls0 ls1)
requires (and (%ral? ls0) (%ral? ls1))
ensures  (boolean? result)

ral-eqv?

[procedure] (ral-eqv? ls0 ls1)

function (result)


(_ ls0 ls1)
requires (and (%ral? ls0) (%ral? ls1))
ensures  (boolean? result)

ral-filter

[procedure] (ral-filter ls ok?)

function (result)


(_ ls ok?)
requires (and (%ral? ls) (procedure? ok?) "(ok? item)")
ensures  (and (%ral? result)
              (fx<= (%ral-count result) (%ral-count ls)))

ral-for-each

[procedure] (ral-for-each ls proc)

command ((old new (constantly #t)))


(_ ls proc)
requires (and (%ral? ls) (procedure? proc) "(proc item)")
ensures  new

ral-from-upto

[procedure] (ral-from-upto ls from upto)

function (result)


(_ ls from upto)
requires (and (%ral? ls) (fixnum? from) (fixnum? upto)
              (fx>= from 0) (fx>= upto from)
              (fx<= upto (%ral-count ls)))
ensures  (and (%ral? result)
              (fx= (%ral-count result) (fx- upto from)))

ral-height

[procedure] (ral-height ls)

function (result)


(_ ls)
requires (%ral? ls)
ensures  (and (fixnum? result) (fx> result 0))

ral-insert!

[procedure] (ral-insert! ls place item)

command ((oldcount newcount (lambda (ls place item) (%ral-count ls)))

        (olditem newitem (lambda (ls place item) (%ral-ref ls place))))

(_ ls place item)
requires (and (%ral? ls) ((%ral-item? ls) item)
              (fixnum? place) (fx>= place 0) (fx<= place (%ral-count ls)))
ensures  (and (fx= newcount (fx+ 1 oldcount)) (equal? newitem item))

ral-item?

[procedure] (ral-item? ls)

function (result)


(_ ls)
requires (%ral? ls)
ensures  (procedure? result)

ral-join

[procedure] (ral-join head tail)

function (result)


(_ head tail)
requires (and (%ral? head) (%ral? tail)
              (eq? (%ral-item? head) (%ral-item? tail)))
ensures  (and (%ral? result)
              (fx= (%ral-count result)
                   (fx+ (%ral-count head) (%ral-count tail))))

ral-level

[procedure] (ral-level ls)

function (result)


(_ ls)
requires (%ral? ls)
ensures  (and (fixnum? result) (fx> result 0)
              (fx< result (%ral-height ls)))

ral-map

[procedure] (or (ral-map ls fn) (ral-map ls fn item?))

function (result)


(_ ls fn)
requires (and (%ral? ls) (procedure? fn) "(fn item)")
ensures  (and (%ral? result)
              (fx= (%ral-count result) (%ral-count ls)))

(_ ls fn item?)
requires (and (%ral? ls) (procedure? fn) "(fn item)"
              (procedure? item?) "(item? item)")
ensures  (and (%ral? result) (fx= (%ral-count result) (%ral-count ls))
              (eq? item? (%ral-item? result)))

ral-max-height

[procedure] (ral-max-height ls)

function (result)


(_ ls)
requires (%ral? ls)
ensures  (and (fixnum? result) (fx> result 0))

ral-node?

[procedure] (ral-node? xpr)

function (result)


(_ xpr)
requires #t
ensures  (boolean? result)

ral-null?

[procedure] (ral-null? ls)

function (result)


(_ ls)
requires (%ral? ls)
ensures  (boolean? result)

ral-place

[procedure] (ral-place ls k)

function (result)


(_ ls k)
requires (and (%ral? ls) (fixnum? k)
              (fx>= k 0) (fx< k (%ral-height ls)))
ensures  (and (fixnum? result) (fx>= result -1)
              (fx< result (%ral-count ls)))

ral-place-next

[procedure] (ral-place-next ls k)

function (result)


(_ ls k)
requires (and (%ral? ls) (fixnum? k)
              (fx>= k 0) (fx< k (%ral-height ls)))
ensures  (and (fixnum? result) (fx>= result 0)
              (fx<= result (%ral-count ls)))

ral-print

[procedure] (ral-print ls)

command ((old new (constantly #t)))


(_ ls)
requires (%ral? ls)
ensures  new

ral-ref

[procedure] (ral-ref ls place)

function (result)


(_ ls place)
requires (and (%ral? ls) (fixnum? place)
              (fx>= place 0) (fx< place (%ral-count ls)))
ensures  ((%ral-item? ls) result)

ral-remove!

[procedure] (ral-remove! ls place)

command ((oldcount newcount (lambda (ls place) (%ral-count ls))))


(_ ls place)
requires (%ral? ls)
ensures  (and (fx= newcount (fx- oldcount 1)))

ral-restructure

<procedure>(or (ral-restructure ls width)

              (ral-restructure ls width max-height))</procedure>

function (result)


(_ ls width)
requires (and (%ral? ls) (fixnum? width) (fx> width 1))
ensures  (and (%ral? result)
              (fx= (%ral-count ls) (%ral-count result))
              (fx= (%ral-width result) width))

(_ ls width max-height)
requires (and (%ral? ls) (fixnum? width)
              (fx> width 1) (fixnum? max-height) (fx> max-height 1))
ensures  (and (%ral? result) (fx= (%ral-count ls) (%ral-count result))
              (fx= (%ral-width result) width)
              (fx= (%ral-max-height result) max-height))

ral-set!

[procedure] (ral-set! ls place item)

command ((old new (lambda (ls place item) (%ral-ref ls place))))


(_ ls place item)
requires (and (%ral? ls) ((%ral-item? ls) item)
              (fixnum? place) (fx>= place 0)
              (fx< place (%ral-count ls)))
ensures  (equal? new item)

ral-split

[procedure] (ral-split ls place)

function (head tail)


(_ ls place)
requires (and (%ral? ls) (fixnum? place)
              (fx>= place 0) (fx< place (%ral-count ls)))
ensures  (and (%ral? head) (%ral? tail)
              (fx= (%ral-count head) place)
              (fx= (%ral-count tail) (fx- (%ral-count ls) place)))

ral-start

[procedure] (ral-start ls)

function (result)


(_ ls)
requires (%ral? ls)
ensures  (%ral-node? result)

ral-width

[procedure] (ral-width ls)

function (result)


(_ ls)
requires (%ral? ls)
ensures  (and (fixnum? result) (fx> result 1))

ral?

[procedure] (ral? xpr)

function (result)


(_ xpr)
requires #t
ensures  (boolean? result)

Examples


;an empty ral of integers
(define ls (make-ral integer?))
(ral? ls)
(ral-null? ls)
(fx= (ral-height ls) 1)

;populate it at the right end
(ral-add! ls 0 1 2 3 4)
(fx= (ral-count ls) 5)
(equal? (ral->list ls) '(0 1 2 3 4))

;remove some items
(ral-remove! ls 2)
(fx= (ral-count ls) 4)
(equal? (ral->list ls) '(0 1 3 4))
(ral-remove! ls (fx- (ral-count ls) 1))
(fx= (ral-count ls) 3)
(equal? (ral->list ls) '(0 1 3))
(ral-remove! ls 0)
(fx= (ral-count ls) 2)
(equal? (ral->list ls) '(1 3))

;insert an item
(ral-insert! ls 1 2)
(fx= (ral-ref ls 1) 2)
(fx= (ral-count ls) 3)
(equal? (ral->list ls) '(1 2 3))

;reset ral
(ral-clear! ls)
(ral-null? ls)

;populate ral again
(do ((k 0 (fx+ 1 k)))
  ((fx= k 100))
  (ral-add! ls k))
(fx= (ral-count ls) 100)

;split, join and subral
(ral-eql? fx=
          ls
          (receive (head tail) (ral-split ls 50)
            (ral-join head tail)))
(equal?
  (ral->list (ral-from-upto ls 20 70))
  (let loop ((k 69) (result '()))
    (if (fx= k 19)
      result
      (loop (fx- k 1) (cons k result)))))

;inspect and change an item in the middle
(fx= (ral-ref ls 50) 50)
(ral-set! ls 50 500)
(fx= (ral-ref ls 50) 500)

;change item back again
(ral-set! ls 50 50)
(fx= (ral-ref ls 50) 50)

;change items at the ends and back again
(ral-set! ls 0 1000)
(fx= (ral-ref ls 0) 1000)
(ral-set! ls 0 0)
(fx= (ral-ref ls 0) 0)
(ral-set! ls 99 1000)
(fx= (ral-ref ls 99) 1000)
(ral-set! ls 99 99)
(fx= (ral-ref ls 99) 99)

;insert at left end
(ral-add-left! ls -1 -2 -3)
(fx= (ral-ref ls 0) -3)
(fx= (ral-ref ls 1) -2)
(fx= (ral-ref ls 2) -1)

;remove them again
(ral-remove! ls 0)
(ral-remove! ls 0)
(ral-remove! ls 0)
(fx= (ral-ref ls 0) 0)
(fx= (ral-count ls) 100)

;insert at right end and remove it again
(ral-add! ls 100 101)
(fx= (ral-ref ls (fx- (ral-count ls) 1)) 101)
(ral-remove! ls (fx- (ral-count ls) 1))
(ral-remove! ls (fx- (ral-count ls) 1))
(fx= (ral-ref ls (fx- (ral-count ls) 1)) 99)

;insert in the middle and remove it again
(ral-insert! ls 20 200)
(fx= (ral-ref ls 20) 200)
(fx= (ral-ref ls 21) 20)
(fx= (ral-count ls) 101)
(ral-remove! ls 20)
(fx= (ral-ref ls 20) 20)
(fx= (ral-count ls) 100)

;restructure
(define lsr (ral-restructure ls 4 20))
(ral-eql? fx= ls lsr)
(fx= (ral-width lsr) 4)
(fx= (ral-max-height lsr) 20)

;map and filter
(equal? (ral->list (ral-map ls add1))
        (let loop ((k 100) (result '()))
          (if (fx= k 0)
            result
            (loop (fx- k 1) (cons k result)))))
(equal? (ral->list (ral-filter ls odd?))
        (let loop ((k 99) (result '()))
          (if (fx< k 0)
            result
            (loop (fx- k 2) (cons k result)))))

Requirements

dbc

Last update

Nov 27, 2013

Author

Juergen Lorenz

License

Copyright (c) 2012-2013, Juergen Lorenz
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the author nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission. 
  
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Version History

0.1.2
tests updated
0.1.1
tests updated
0.1
initial import