We need two definitions to make streams work. First, delay is a macro that replaces a function with an anonymous function with it's body equal to the function. Later when we need to evaluate the function, we only have to call the anonymous function. This is what yield does. Yield isn't as necessary, but it makes it clear what's happening.
macro delay(f)
return quote
# put the function inside a lambda
function ()
$f
end
end
end
function yield(delayed_function)
delayed_function() # call it
end
something = @delay(println("text"))
yield(something)
Now with that done we create a stream type. The first element is the actual value held there. The second element is a delayed stream constructor. We then create head and tail to access elements of the stream. Nth takes successive head and tails of a stream.
type IntStream
literal
next
function IntStream(num)
self = new()
self.literal = num
self.next = @delay(IntStream(num+1))
return self
end
end
function head(item::IntStream)
return item.literal
end
function tail(item::IntStream)
return yield(item.next)
end
integers = IntStream(1)
head(integers)
head(tail(tail(integers)))
function nth(stream, n)
if n == 1
return head(stream)
else
return nth(tail(stream), n-1)
end
end
nth(integers, 50)
For more general streams it is simple to add a next method for each object.
type Stream
obj
next
function Stream(obj, next) # next is the method for generating the next object
self = new()
self.obj = obj
self.next = @delay(Stream(next(obj), next))
return self
end
end
function head(item::Stream)
return item.obj
end
function tail(item::Stream)
return yield(item.next)
end
function nth(stream, n)
if n == 1
return head(stream)
else
return nth(tail(stream), n-1)
end
end
Now to try it on the Fibonacci numbers. Instead of making each element the nth Fibonacci number we'll make it the list of the first n+1 Fibonacci numbers.
function next_fib_list(list)
return [ list, (list[end] + list[end-1]) ]
end
fib_list = Stream([0, 1], next_fib_list)
nth(fib_list, 5)
nth(fib_list, 10)
for idx = 1:15
println(nth(fib_list, idx))
end
It is also possible to memoize the function calls so that expensive operations aren't run more than once.
No comments:
Post a Comment