; Implementation of SICP streams (lazy-evaluation lists) in Logo.

; Since we don't have special forms, we put the second argument to STREAM
; in a (quoted) list.

; Since we don't have lexical scope, we use substitution (`) into saved
; expressions.

to stream :car :delayed.cdr
output fput :car (list "*delayed* :delayed.cdr)
end

to head :stream
output first :stream
end

to tail :stream
if emptyp bf :stream [output []]
if not equalp first bf :stream "*delayed* [output bf :stream]
localmake "result run last :stream
.setbf :stream :result
output :result
end

; higher order functions for streams

; Remember that if the functional argument uses local variables, it has to
; be backquoted.

to stream.map :fun [:streams] 2
if emptyp first :streams [output []]
output stream apply :fun firsts :streams ~
           `[(apply "stream.map fput ,[quoted :fun] (map "tail ,[:streams]))]
end

to stream.filter :fun :stream
if emptyp :stream [output []]
if invoke :fun head :stream ~
  [output stream head :stream `[stream.filter ,[quoted :fun] tail ,[:stream]]]
output stream.filter :fun tail :stream
end

to flatten :stream.of.streams
if emptyp :stream.of.streams [output []]
output flatten1 head :stream.of.streams :stream.of.streams
end

to flatten1 :stream :delayed.more.streams
if emptyp :stream [output flatten tail :delayed.more.streams]
output stream (head :stream) ~
              `[flatten1 tail ,[:stream]
                         ,[:delayed.more.streams]]
end

; helper for debugging

to show.stream :stream [:num 10]
show show.stream1 :stream :num
end

to show.stream1 :stream :num
if emptyp :stream [output []]
if equalp :num 0 [output [...]]
output fput head :stream (show.stream1 tail :stream :num-1)
end

; examples

to integers.from :n
output stream :n `[integers.from ,[:n+1]]
end

make "integers integers.from 1

to sieve :stream
output stream (head :stream) ~
              `[sieve stream.filter [not divisiblep ? ,[head :stream]]
                                    tail ,[:stream]]
end

to divisiblep :big :small
output 0 = remainder :big :small
end

make "primes sieve tail :integers
