Nothing obvious, no. But Racket is designed to encourage programming
without mutation, and doubly linked lists require mutation.
It's very likely a zipper will do what you want. A zipper is much easier
to implement than a doubly linked list, and has similar performance and
uses.
Here's a quick implementation:
#lang racket
(struct zipper (head current tail) #:transparent)
(define (zipper-next z)
(match-define (zipper h c t) z)
(zipper (cons c h) (first t) (rest t)))
(define (zipper-prev z)
(match-define (zipper h c t) z)
(zipper (rest h) (first h) (cons c t)))
(define (zipper-set z new-current)
(match-define (zipper h _ t) z)
(zipper h new-current t))
(define (list->zipper lst)
(zipper empty (first lst) (rest lst)))
(define (zipper->list z)
(match-define (zipper h c t) z)
(append (reverse h) (list c) t))
; tests: replace 2 and 3 with 20 and 30
(define z1 (list->zipper '(1 2 3 4)))
(define z2 (zipper-next z1))
(define z3 (zipper-set z2 20))
(define z4 (zipper-next z3))
(define z5 (zipper-next z4))
(define z6 (zipper-prev z5))
(define z7 (zipper-set z6 30))
(zipper->list z7)
(zipper->list z3) ; just the 2 is different
A zipper is like a cursor. It has a current element, the stuff before
the current element (head), and the stuff after (tail). There are
functions to move forward (zipper-next) and back (zipper-prev). Both
return a new zipper, with a different current element.
The only tricky part is that the head is stored in reverse order to make
it easier to grab the "last" element in the head in zipper-prev. In
reverse order, the "last" element is the first.
Neil T
_________________________________________________
For list-related administrative tasks:
http://lists.racket-lang.org/listinfo/users
To supplement, here are very old notes I wrote to myself on the kind
of list zipper that Neil is presenting:
https://hkn.eecs.berkeley.edu/~dyoo/plt/zippers.txt
Thank you very much for this nice intermediate solution, though I need constant-time append, split, insert, remove, + pointers to items, etc. Mutation does seem unavoidable, right.
The "zipper" structure Neil posted has constant-time append if you're already at the head of one zipper and the tail of the other, but in general it'll be linear time. It has constant-time split, insert, and remove.On Aug 30, 2011, at 3:18 AM, Laurent wrote:Thank you very much for this nice intermediate solution, though I need constant-time append, split, insert, remove, + pointers to items, etc. Mutation does seem unavoidable, right.
What do you mean by "pointers to items" -- that is, what do you need to DO with pointers to items?
You are stating: hash with bounded memoryDoes that mean you will have a limit on the number of entries?
In that case a vector plus a current index might do, I think.Gives you O(1) access to every element.If the required number of elements may vary very much, a vector probably is not a good idea, unless using resizable vectors, but that makes some operations O(n).
Would you lso want entries to be garbage-collected? If there are no
longer any references to a hash entry's key except as a key in the hash
table, would you want the garbage colelctor to be able to remove it?
-- hendrik
Would you lso want entries to be garbage-collected? If there are no
longer any references to a hash entry's key except as a key in the hash
table, would you want the garbage colelctor to be able to remove it?
- Random Access Lists implemented in Hari Prashanth's Typed Purely
Functional Data Structures [1]
Some of the other data structures here, such as VLists, may also be helpful.
- David Van Horn's RAList package [2]
[1] http://planet.plt-scheme.org/package-source/krhari/pfds.plt/1/5/planet-docs/functional-data-structures/Random_Access_Lists.html
[2] http://planet.racket-lang.org/display.ss?package=ralist.plt&owner=dvanhorn
--
sam th
sa...@ccs.neu.edu
On 08/30/11 09:18, Laurent wrote:
> Thank you very much for this nice intermediate solution, though I need
> constant-time append, split, insert, remove, + pointers to items, etc.
> Mutation does seem unavoidable, right.
I implemented a doubly-linked list, not so long ago, connected to a GUI
that can insert and delete items and saw no way to make the list
functional with multiple simultaneous editors in the GUI. The
implementation is as a straightforward cyclical doubly-linked list. I
toyed with the idea of having a separate handle object to represent the
list versus just the nodes, and there are some rudiments of that left in
the code, but in the end the user code uses a special 'top element to
indicate where the cyclical list is supposed to start.
Good luck,
Marijn
Vincent
At Wed, 31 Aug 2011 11:46:34 +0200,
Marijn wrote:
>
> [1 <multipart/signed (7bit)>]
> [1.1 <multipart/mixed (7bit)>]
> [1.1.1 <text/plain; ISO-8859-1 (quoted-printable)>]
> [1.1.2 dlist.rkt <text/plain (base64)>]
> (module dlist racket
> (provide dlist dl-insert dl-insert-right dl-remove for/dlist)
>
> (require (for-syntax racket))
>
> (define (dl-print dl p write?)
> (let ((print (if write? write display)))
> (display #\( p)
> (let loop ((l dl))
> (print (_dl-val l) p)
> (let ((right (_dl-right l)))
> (if (eq? right dl)
> (display #\) p)
> (begin (display " " p) (loop right)) )))))
>
> (define (dl-sequence l)
> (if (dl-empty? l)
> (make-do-sequence (lambda () (values #f #f #f (lambda (lk) #f) #f #f)))
> (let ((last (_dl-left l)))
> (make-do-sequence
> (lambda () ; val next start last?
> (values _dl-val _dl-right l #f #f (lambda (lk v) (not (eq? lk last)))) )))))
>
> ;;; link
> (define-struct _dl (left val right) #:mutable
> #:property prop:custom-write dl-print
> #:property prop:sequence dl-sequence
> ) ; end link
>
> (define (dlh-print dlh p write?)
> (dl-print (_dlh-link dlh) p write?))
>
> (define (dlh-sequence l)
> (let ((h (_dlh-link l)))
> (make-do-sequence
> (lambda () ; val next start last?
> (values _dl-val _dl-right (_dl-right h) (lambda (lk) (not (eq? lk h))) #f #f) ))))
>
> ;;; list handle
> (struct _dlh (link) #:mutable
> #:property prop:custom-write dlh-print
> #:property prop:sequence dlh-sequence
> ) ; end handle
>
> (define (dl-empty)
> (_dl #f #f #f))
>
> (define (dlh-empty)
> (_dlh (dl-empty)))
>
> (define (dl-empty? l)
> (not (_dl-left l)))
>
> (define (dl-one-element? l)
> (eq? l (_dl-left l)))
>
> (define (dlh-empty? l)
> (dl-empty? (_dlh-link l)))
>
> ; (define (dlist a b c)
> ; (shared ((la (_dl #f a lb))
> ; (lb (_dl la b lc))
> ; (lc (_dl lb c #f)) )
> ; la))
>
> (define-syntax (dlist stx)
> (syntax-case stx ()
> ((_) #'(dl-empty))
> ((_ a b ...)
> (let* ((temps (generate-temporaries #'(a b ...))) (links `(,(last temps) ,@temps ,(first temps))))
> #`(shared
> #,(let loop ((ret '()) (links links) (vals (syntax->list #'(a b ...))))
> (if (empty? vals) (reverse ret)
> (loop (cons #`(#,(cadr links) (make-_dl #,(car links) #,(car vals) #,(caddr links))) ret)
> (cdr links) (cdr vals) )))
> #,(cadr links))))))
>
> (define-syntax-rule (dlisth a b ...) (_dlh (dlist #f a b ...)))
>
> (define-syntax-rule (_dl-insert val link link-next new-link set-link-next! set-link-prev!)
> (if (dl-empty? link) (dlist val)
> (let* ((next (link-next link)) (new (new-link link val next)))
> (set-link-next! link new)
> (and next (set-link-prev! next new))
> new)))
>
> (define (dl-insert-right v l)
> (_dl-insert v l _dl-right _dl set-_dl-right! set-_dl-left!))
>
> (define (dl-insert v l)
> (let-syntax ((dl (syntax-rules () ((_ r v l) (_dl l v r)))))
> (_dl-insert v l _dl-left dl set-_dl-left! set-_dl-right!)))
>
> (define-syntax-rule (_dlh-insert v l insert)
> (let ((h (_dlh-link l)))
> (if h
> (insert v h)
> (set-_dlh-link! l (dlist v)) )))
>
> (define (dlh-insert-front v l)
> (_dlh-insert v l dl-insert-right))
>
> (define (dlh-insert-back v l)
> (_dlh-insert v l dl-insert))
>
> (define (dl-remove link (ret #f))
> (if (or (dl-empty? link) (dl-one-element? link))
> (dl-empty)
> (let ((l (_dl-left link)) (r (_dl-right link)))
> (set-_dl-right! l r)
> (set-_dl-left! r l)
> (if ret l r))))
>
> (define (dl-reverse link)
> (if (dl-empty? link) (dl-empty)
> (let ((left (_dl-left link)) (right (_dl-right link)))
> (set-_dl-right! link left)
> (set-_dl-left! link right)
> (let loop ((lft link) (lnk right))
> (if (eq? lnk link) left
> (let ((rght (_dl-right lnk)))
> (set-_dl-right! lnk lft)
> (set-_dl-left! lnk rght)
> (loop lnk rght)))))))
>
> ; (define (dlh-reverse l)
>
> (define-syntax-rule (for/dlist clauses body ... val)
> (_dl-right (for/fold ((ret (dl-empty))) clauses (dl-insert-right val ret))))
>
> ) ; end module
> [1.1.3 list-editor.rkt <text/plain; UTF-8 (quoted-printable)>]
> #lang racket/gui
>
> ;(require dlist)
> (require "./dlist.rkt")
>
> (define list-editor%
> (class vertical-panel%
> (init init-values parent)
> (super-new (parent parent))
>
> (define widget-list (dlist 'top))
>
> (define (redisplay)
> (send this change-children (lambda (l) (cdr (for/list ((w widget-list)) w)))))
>
> (define (insert-item val link)
> (let* ((v (new vertical-panel% (parent this)))
> (lk (dl-insert v link))
> (ins (new button% (parent v) (label "insert")
> (callback (λ (b e)
> (insert-item "1" lk) (redisplay) )) ) )
> (h (new horizontal-pane% (parent v)))
> (t (new text-field% (parent h) (label "") (init-value val)))
> (del (new button% (parent h) (label "del")
> (callback (λ (b e) (dl-remove lk) (send this delete-child v))) )))
> lk))
>
> ; (send this begin-container-sequence)
> (for ((v init-values)) (insert-item v widget-list))
> ; (send this end-container-sequence)
>
> (let* ((v (new vertical-panel% (parent this)))
> (lk (dl-insert v widget-list)))
> (new button% (parent v) (label "append")
> (callback (λ (b e) (insert-item "1" lk) (redisplay))) ))
>
> )) ; end define class
>
> (define root (new frame% (label "List Editor") (stretchable-height #f)))
>
> (new list-editor% (parent root) (init-values '("1" "2" "3")))
>
> (send root show #t)
> [1.2 OpenPGP digital signature <application/pgp-signature (7bit)>]
>
> [2 <text/plain; us-ascii (7bit)>]
Hi Vincent,
On 08/31/11 17:50, Vincent St-Amour wrote:
> Are you planning to put this on PLaneT?
>
> Vincent
I wasn't. This code has seen very little testing and has so far only
been used in toy/throw-away code. The Gambit community has a
no-expectation-of-quality place to dump code, appropriately called the
Dumping Grounds[1]. If this was the gambit mailing list I would have
said "sure I'll dump it", but I don't think I am ready to inflict it
on Planet. Maybe my expectations of code on Planet is off, or maybe
racket also needs a Dumping Grounds...
Marijn
[1]:http://dynamo.iro.umontreal.ca/~gambit/wiki/index.php/Dumping_Grounds
> At Wed, 31 Aug 2011 11:46:34 +0200, Marijn wrote:
>>
>> [1 <multipart/signed (7bit)>] [1.1 <multipart/mixed (7bit)>]
>> [1.1.1 <text/plain; ISO-8859-1 (quoted-printable)>] Hi Laurent,
>>
>> On 08/30/11 09:18, Laurent wrote:
>>> Thank you very much for this nice intermediate solution, though
>>> I need constant-time append, split, insert, remove, + pointers
>>> to items, etc. Mutation does seem unavoidable, right.
>>
>> I implemented a doubly-linked list, not so long ago, connected to
>> a GUI that can insert and delete items and saw no way to make the
>> list functional with multiple simultaneous editors in the GUI.
>> The implementation is as a straightforward cyclical doubly-linked
>> list. I toyed with the idea of having a separate handle object to
>> represent the list versus just the nodes, and there are some
>> rudiments of that left in the code, but in the end the user code
>> uses a special 'top element to indicate where the cyclical list
>> is supposed to start.
>>
>> Good luck,
>>
>> Marijn
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.18 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk5grQsACgkQp/VmCx0OL2yB7ACfVmnT7S7y0pvTvUa7mtkRg8EN
GRwAn0DHnPxrnT/+we0gIuvufQRMv3SF
=fD70
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi Vincent,
I wasn't. This code has seen very little testing and has so far only
On 08/31/11 17:50, Vincent St-Amour wrote:
> Are you planning to put this on PLaneT?
>
> Vincent
been used in toy/throw-away code. The Gambit community has a
no-expectation-of-quality place to dump code, appropriately called the
Dumping Grounds[1]. If this was the gambit mailing list I would have
said "sure I'll dump it", but I don't think I am ready to inflict it
on Planet. Maybe my expectations of code on Planet is off, or maybe
racket also needs a Dumping Grounds...
-----Original Message-----
From: Laurent [mailto:laurent...@gmail.com]
Sent: 02 September 2011 13:25
To: Marijn; Jukka Tuominen
Cc: Vincent St-Amour; us...@racket-lang.org
Subject: Re: [racket] doubly linked list lib