Streams IIIA o Data organization: like a signal processing system: o Abstractions over data management patterns. o Programming as a circuit design process with high level components. o Clarity, modularity, reusability. Initial Value Initial Stream Integral TAPIA 2005-2006 Example I IIIA o Function to add the square of the values of the odd leaves of a tree. (defun add-square-odd (tree) (if (leaf? tree) (if (odd? tree) (square tree) 0) (+ ( add-square-odd (left-tree tree)) ( add-square-odd (right-tree tree))))) TAPIA 2005-2006 Example II IIIA o Function to list the odd Fibonacci numbers fib(0) = 0 fib(1) = 1 fib(n) = fib(n-1) +fib(n-2) 0,1,1,2,3,5,8,13,... (defun odd? (x) (not (= (mod x 2) 0))) (defun fib (x) (case x (0 0) (1 1) (t (+ (fib (- x 1)) (fib (- x 2)))))) (defun list-odd-fibs (n) (defun next (k) (unless (> k n) (let ((f (fib k))) (if (odd? f) (cons f (next (1+ k))) (next (1+ k)))))) (next 1)) TAPIA 2005-2006 Engineering IIIA o add-square-odd o o o o Enumerate the leaves of a tree Filter selecting the odd ones Compute the squares of the selected Accumulate the results ENUMERATE leaves tree FILTER odd? MAP square ACCUMULATE +, 0 o List-odd-fibs o o o o Enumerate the integers from 1 to N Compute the corresponding Fibonacci numbers Filter selecting the odd ones Accumulate the results on a list ENUMERATE integers TAPIA 2005-2006 MAP fib FILTER odd? ACCUMULATE cons, nil Stream model IIIA o Previous programs: o Enumeration and selection: odd? function and recursive call o Accumulation: in the test and recursive call o Previous functions are a mix of abstract components o STREAM TECHNIQUE GOAL: to built programs where the flow of the signal is explicit o Advantatges: o Conceptual clarity of the code o Modularity and reusability: filter, map, accumulate (second order?) o Signals are implemented as data structures called STREAMS: a sequence of elements TAPIA 2005-2006 IIIA (Abstract Data Type) Stream Independent of the implementation Domain: Elements, Streams, Bool Functions: the-empty-stream: Stream cons-stream: Element x Stream Æ Stream head: Stream Æ Element tail: Stream Æ Stream empty-stream?: Stream Æ Bool Axioms: head(cons-stream(a,S)) = a tail(cons-stream(a,S)) = S empty-stream?(the-empty-stream) = true Syntax Semantics We can change the implementation without changing the syntax and the semantics TAPIA 2005-2006 First implementation IIIA Possible LISP implementation: dotted pairs or lists the-empty-stream cons-stream head tail empty-stream? () CONS FIRST REST NULL (defconstant the-empty-stream nil) (defun empty-stream? (s) (null s)) (defun head (s) (car s)) (defun cons-stream (a b) (cons a b)) (defun tail (s) (cdr s)) TAPIA 2005-2006 Example I IIIA (defun enumerate-tree (tree) (if (leave? tree) (cons-stream tree the-empty-stream) (append-streams (enumerate-tree (left-tree tree)) (enumerate-tree (right-tree tree))))) (defun append-streams (s1 s2) (if (empty-stream? s1) s2 (cons-stream (head s1) (append-streams (tail s1) s2)))) (defun filter-odd (s) (cond ((empty-stream? s) the-empty-stream) ((odd? (head s)) (cons-stream (head-s) (filter-odd (tail s)))) (t (filter-odd (tail s))))) TAPIA 2005-2006 Example I IIIA (defun map-square (s) (if (empty-stream? s) the-empty-stream (cons-stream (square (head s)) (map-square (tail s))))) (defun acumulate-+ (s) (if (empty-stream? s) 0 (+ (head s) (acumulate-+ (tail s))))) (defun add-square-odd (tree) (if (leaf? tree) (if (odd? tree) (square tree) 0) (+ ( add-square-odd (left-tree tree)) ( add-square-odd (right-tree tree))))) (defun add-square-odd (tree) (acumulate-+ (map-square (filter-odd (enumerate-tree tree))))) TAPIA 2005-2006 Reusability IIIA o Similarly with the other function … (defun list-odd-fibs (n) (acumulate-cons (filter-odd (map-fib (enumerate-interval 1 n))))) o Using the same functions: addition of the squares of the n firsts numbers of Fibonacci. TAPIA 2005-2006 (defun list-square-fib (n) (accumulate-cons (map-square (map-fib (enumerate-interval 1 n))))) Higher order functions IIIA (defun accumulate (combiner init stream) (if (empty-stream? stream) init (defun sum-stream (stream) (funcall combiner (accumulate #'+ 0 stream)) (head stream) (defun prod-stream (stream) (accumulate combiner init (accumulate #'* 1 stream)) (tail stream))))) (defun concat-stream (stream) (accumulate #'cons nil stream)) (defun filter (pred stream) (cond ((empty-stream? stream) the-empty-stream) ((funcall pred (head stream)) (cons-stream (head stream) (filter pred (tail stream)))) (t (filter pred (tail stream))))) TAPIA 2005-2006 First summary IIIA o Pros: o Streams can be considered the standard interface among programs o Stream programming is elegant and produces compact code. o Cons: o The current implementation is very inefficient in time and memory TAPIA 2005-2006 IIIA Efficiency (defun add-primes (a b) (labels ((iterate (cont acum) (cond ((> cont b) acum) ((prime? cont) (iterate (1+ cont) (+ cont acum))) (t (iterate (1+ cont) acum))))) (iterate a 0))) (defun add-primes (a b) (accumulate '+ 0 (filter prime? (enumerate-interval a b)))) Consider: (head (tail (filter primo? (enumerate-interval 10000 1000000)))) We have to generate and memorize 1.000.000 integers! TAPIA 2005-2006 IIIA Lazy evaluation o Elegance and efficiency of streams is based on lazy evaluation. o IDEA: implementation of CONS-STREAM that builds partially a stream. The stream will be built “on demand” (defmacro delay (exp) `(function (lambda () ,exp))) (defun force (exp) (funcall exp)) Second implementation TAPIA 2005-2006 (defconstant the-empty-stream nil) (defun empty-stream? (s) (null s)) (defmacro cons-stream (a b) `(cons ,a (delay ,b))) (defun head (s) (first s)) (defun tail (s) (force (rest s))) Enumerate-interval IIIA (defun enumerate-interval (x y) (unless (> x y) (cons-stream x (enumerate-interval (+ x 1) y)))) o Without delay o Overflow 1000 o Overflow 10000 compiled (compile ‘enumerate-interval) o Continuation? Tail recursive. o With delay o (enumerate-interval 1 10) Æ (1 . #<closure SPECIAL>) o (tail (enumerate-interval 1 10) Æ (tail (1 . #<closure SPECIAL>)) (force (rest (1 . #<closure SPECIAL>))) Æ (force (#<closure SPECIAL>)) Æ (funcall ... TAPIA 2005-2006 IIIA Delayed evaluation, efficiency (head (tail (filter odd? (enumerate-interval 10000 1000000)))) (enumerate-interval 10000 1000000) (cons 10000 (delay (enumerate-interval 10001 1000000))) (1 …) (defun filter (pred stream) (cond ((empty-stream? stream) the-empty-stream) ((funcall pred (head stream)) (cons-stream (head stream) (filter pred (tail stream)))) (t (filter pred (tail stream))))) TAPIA 2005-2006 IIIA Delayed evaluation, efficiency o Filter: head (10000) is even, then it force the evaluation of the tail of the stream (cons 10001 (delay (enumerate-interval 1002 1000000))) o The final result is 10003 o The elements generated are those strictly necessary to obtain the result o Lazy: eval when necessary TAPIA 2005-2006 IIIA LISP: lexical closures o Lexical closure: a function that, when invoked on arguments, executes the body in the lexical environment that was captured at the time of the creation of the lexical closure, augmented by bindings of the function's parameters to the corresponding arguments. o Lexical environment: that part of the environment that contains bindings whose names have lexical scope. o Dynamic environment (let ((x 0)) (defun f (y) (setf x (1+ x)) TAPIA 2005-2006 Improvement: Memoization IIIA o Lazy evaluation: call-by-name (Algol 60) o Do not evaluate the same argument many times o Memoizing: call-by-need, to put into memory the results of evaluation o Table of argument/result TAPIA 2005-2006 IIIA Memoization (defmacro memo-proc (proc) `(let ((already-run? nil) (result nil)) #’(lambda () (if (not already-run?) (block then (setq result (,proc)) (setq already-run? t) result) result)))) Third implementation (defmacro delay (exp) `(memo-proc (lambda () ,exp))) o Force is the same function TAPIA 2005-2006 Lexical Closures IIIA o Lexical closures are dangerous: side-effects with the assignment of its variables (defun lex-clos () (let ((x 0) (a the-empty-stream)) (setq a (cons-stream 3 (cons-stream x a))) (pprint (head (tail a))) (setq x 1) (pprint (head (tail a))))) with memoizing: ==> 0 ==> 1 without memoizing: ==> 0 ==> 0 TAPIA 2005-2006 Infinite Streams IIIA o Streams are only evaluated “component to component” by demand o Infinite streams o Infinite recursion based on streams (defun integers-starting-from (n) (cons-stream n (integers-starting-from (1+ n)))) (defun integers () (integers-starting-from 1)) TAPIA 2005-2006 IIIA Infinite Streams o Definition of all the “no-divisible by 7” integers (defun divisible? (x y) (= (rem x y) 0)) (defun no-sevens (filter #'(lambda (x) (not (divisible? x 7))) integers)) (defun filter (pred stream) (cond ((empty-stream? stream) the-empty-stream) ((funcall pred (head stream)) (cons-stream (head stream) (filter pred (tail stream)))) (t (filter pred (tail stream))))) TAPIA 2005-2006 IIIA Infinite Streams o With the nth-stream function (defun nth-stream (n s) (if (= n 0) (head s) (nth-stream (1- n) (tail s)))) (nth-stream 100 no-sevens) Æ 117 TAPIA 2005-2006 Implicit Streams IIIA o Interesting definition of Fibonacci numbers o Recursive definition of variables! (setq fibs (cons-stream 0 (cons-stream 1 (add-streams (tail fibs) fibs)))) (defun add-streams (s1 s2) (cond ((empty-stream? s1) s2) ((empty-stream? s2) s2) (t (cons-stream (+ (head s1) (head s2)) (add-streams (tail s1) (tail s2)))))) (0 1 (add-streams (1 ...)(0 ...)) (0 1 1 (add-streams (1 ...)(1 ...)) (0 1 1 2 (add-streams (2 ...)(1 ...)) (0 1 1 2 3 (add-streams ... TAPIA 2005-2006 Experiments IIIA o Comparison of delay techniques o Call-by-need o (time (nth-stream 1000 fibs)) Æ 0.417 s o Call-by-name o (time (nth-stream 20 fibs)) Æ 10.383 s o Compilation of nth-stream (call-by-need, tail recursive) o (time (nth-stream 10000 fibs)) Æ 2.3 s o (time (nth-stream 10000 fibs)) Æ 0.3 s (compiled) o Other implicit definitions o (setq ones (cons-stream 1 ones)) o (setq integers (cons-stream 1 (add-streams ones integers))) TAPIA 2005-2006 Sieve of Eratosthenes IIIA Sieve Head Tail cons Filter: Not Divisible? Sieve Signal processing technique applied to Sieve (defun sieve (stream) (cons-stream (head stream) (sieve (filter #'(lambda (x) (not (divisible? x (head stream)))) (tail stream))))) (setf primes (sieve (integers-starting-from 2))) (2 3 4 5 ... (2 3 4 5 6 7 8 9 ... (2 3 5 7 9 ... (2 3 5 7 9 ... TAPIA 2005-2006 IIIA Summary o Modular and homogeneous programming of functions o Delay and force are the main functions for lazy evaluation o Lazy evaluation = call-by-need + lazy constructors o State without assignment o Infinite concepts, recursively defined o Functional input/output: stream interface TAPIA 2005-2006 State representation IIIA o Stream of withdraws (defun make-withdraw (balance) #'(lambda (amount) (setq balance (- balance amount)) balance) (defun stream-withdraw (balance amount-stream) (cons-stream balance (stream-withdraw (- balance (head amount-stream)) (tail amount-stream)))) TAPIA 2005-2006 Stream problems IIIA 1. Stream consumption 2. Difficulty to represent relations Bank account: Joan Merge Account Maria (a1 b1 a2 b2 a3 ...) o Solution: non functional component, non deterministic merge. Wait for input in the streams and consume it immediately. Problem: non functional time concept. TAPIA 2005-2006 IIIA Relations o In the following relation there is no A+B=C an input/output relation. o Restrictions over the values of A, B y C. o Signal processing model is not useful TAPIA 2005-2006
© Copyright 2026 Paperzz