[Haskell-cafe] Re: Doubly-linked zipper list w/ insert implementation

2007-11-10 Thread apfelmus

Justin Bailey wrote:

The other day I decided to implement a ring buffer with a current
element (i.e. a doubly-linked zipper list).

[...]

p.s. The original motivation for writing this was to model cellular
automata. The CA world is circular, so that got me thinking about a
structure that made connecting the ends easy to do.


Note that depending on your concrete setting, you may not need a fancy 
ring structure for cellular automata. And with simple automata like


  c'_i = c_(i-1) `xor` c_i `xor` c_(i+1)

it may even be easier to generate fresh rings for each step in the 
automaton:


  data Context a = Context [a] a [a]
  -- rotate left
  rotL (Context ls x (r:rs)) = Context (x:ls) r rs

  -- description of a cellular automaton
  type Rule a= Context a - a
  example :: Rule Bool
  example (Context (cm:_) c (cp:_)) = cm `xor` c `xor` cp

  -- run a cellular automaton on an initial band of cells
  --   which is considered to be cyclic, i.e. a cylinder
  automate :: Rule a - [a] - [[a]]
  automate f xs = iterate (take n . map f . mkContexts) xs
where
-- length of the cell band
n = length xs

mkContexts (x:xs)= iterate rotL $
Context (cycle $ reverse xs) (head xs) (tail $ cycle xs)

Here,  mkContexts xs  initializes a new infinite cyclic ring for  xs 
and rotates it left ad infinitum.



Regards,
apfelmus

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Re: Doubly-linked zipper list w/ insert implementation

2007-11-10 Thread Justin Bailey
On Nov 10, 2007 12:24 PM, apfelmus [EMAIL PROTECTED] wrote:
 Note that depending on your concrete setting, you may not need a fancy
 ring structure for cellular automata. And with simple automata like

I realized that I never updated my automata once a row was created,
and ended up using an unboxed array with an index to represent the
ring. I just do some math when I want to rotate left or right and
the index falls off the edge.

The rules are much more complex though. I am using a genetic algorithm
technique to evolve 7 bit rules which can classify if an initial row
was mostly black or mostly white. This is loosely related to a class
assignment.

I'm finding that taking 100 initial rules, determining their fitness
on 100 initial automatas, and doing that for 100 generations is taking
a lng time. Our teacher's implementation, in C, does it in about a
minute. Mine takes hours :( . I think its becuase the C algorithm does
a lot of bit-twiddling to iterate the automata, while I'm using lists
of integers (1, 0).

Anyways, thanks for your thoughts!

Justin
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


[Haskell-cafe] Re: Doubly-linked zipper list w/ insert implementation

2007-11-07 Thread apfelmus

Justin Bailey wrote:

The other day I decided to implement a ring buffer with a current
element (i.e. a doubly-linked zipper list). In order to allow inserts
(and, in the future, deletes and updates), I have a special sentinel
element called Join in the structure. When inserting, I find the
join first, insert and then rebuild the buffer using circular
programming techniques. This also allows the buffer to be converted
back to a list. The current element can be changed by rotating right
or left, which never fails. Rotating n positions takes n steps.

I'm posting it here for comments and feedback. How could the structure
be smarter? Would storing a unique ID with each element make more
sense? Any comments on the space behavior under insert and rotates? I
wanted to maximize sharing. Thanks in advance.


Do you really need to realize the cycle by sharing? I mean, sharing 
doesn't go well with insertion / updates / deletion since each of these 
operations breaks it and needs to restore it everywhere. In other words, 
your  insert  takes O(n) time. I'd simply drop the sharing and use two 
double ended queues (or something like that) instead


  data Ring a = Ring (DeQueue a) a (DeQueue a)

-- pseudo-code missing lots of cases. I want views!
  left (Ring (l' : ls : l) x (r : rs : r')) =
Ring (ls : l : x) r (rs : r' : l')

This way, you can implement update operations in O(1) time instead of 
O(n). With a fancy random access queue like Data.Sequence , you can even 
have rotations like  rotL xs n  in O(log n) time.


(I keep mixing up the meaning of  rotL  and  rotR , does L push the 
current element to the left or does it rotate the ring clockwise?)



Regards,
apfelmus

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] Re: Doubly-linked zipper list w/ insert implementation

2007-11-07 Thread Justin Bailey
On Nov 7, 2007 10:16 AM, apfelmus [EMAIL PROTECTED] wrote:

 Do you really need to realize the cycle by sharing? I mean, sharing
 doesn't go well with insertion / updates / deletion since each of these
 operations breaks it and needs to restore it everywhere. In other words,
 your  insert  takes O(n) time. I'd simply drop the sharing and use two
 double ended queues (or something like that) instead

Very good point, and much easier to implement with Data.Sequence to
boot. All that circular programming made my brain hurt.

Thanks for your feedback.

Justin
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe