practice session #6: 1. sequence operations 2. partial evaluation with currying 3. lazy-lists
TRANSCRIPT
2
Motivation: The procedure signs maps a collection of numbers to their sign.
1. The sequence operations
For (int i=0; i<arrayLength; i++) { if (a[i] == 0) b[i] = 0 ; else b[i] = a[i]/abs(a[i]);}
In C:
Signature: signs(ls)Example: (signs(list 1 2 0 -3 4 -5 6))=> '(1 1 0 -1 1 -1 1)Type: [List(Number)->List(Number)](define signs (lambda (ls) (if (null? ls) ls (if (= 0 (car ls)) (cons 0 (signs (cdr ls)))) (cons (/ (car ls) (abs (car ls))) (signs (cdr ls)))))))
In Scheme:
3
Motivation: The procedure signs maps a collection of numbers to their sign.
1. The sequence operations
Signature: map(proc,items)Purpose: Apply 'proc' to all 'items'.Type: [[T1 -> T2]*List(T1) -> List(T2)]
The sequence operation map:
Signature: signs-map(ls)Example: (signs(list 1 2 0 -3 4 -5 6))=> '(1 1 0 -1 1 -1 1)Type: [List(Number)->List(Number)](define signs-map (lambda (ls) (map (lambda (x) (if (= x 0) 0 (/ x abs(x)))) ls)))
Implementation using sequence operations:
4
1. The sequence operations
The procedure partition divides a list into two lists according to some predicate. For example:
> (partition number? (list 1 2 ‘a 4 ‘b #t))‘((1 2 4) ‘a ‘b #t)
Implement the procedure partition, using the sequence operation filter.
The sequence operation filter:
Signature: filter(predicate, sequence)Purpose: return a list of all elements satisfying the predicate.Type:[[T-> Boolean]*List(T) -> List(T)]Post-condition: x in result <=> x in sequence, and (eq? #t (predicate x))
Additional sequence operations:
5
1. The sequence operations
The procedure partition, using the sequence operation filter:
Signature: partition(predicate, sequence)Type: [[T-> Boolean]*List(T) -> Pair(List(T),List(T))]Example: (partition number? (list 1 (cons 1 2) 3 4 (cons 0))) => '((1 3 4) (1 . 2) (0 . 3)) (partition number? (list 1 2 'a 'b 3 4 'd #t)) => '((1 2 3 4) a b d #t)
(define partition (lambda (pred seq) (cons (filter pred seq) (filter (lambda (x) (not (pred x))) seq))))
Additional sequence operations:
6
1. The sequence operations
For example:
> (accumulate + 0 (list 1 2 3 4 5))15
> (accumulate expt 1 (list 4 3 2))262144
The sequence operation accumulate:
Signature: accumulate(op,initial,sequence)Purpose: Accumulate by 'op' all sequence elements, starting (ending) with 'initial'Type: [[T1*T2 -> T2]*T2*List(T1) -> T2]Post-condition: result=(initial op (seq[n] (op ...(op seq[1]))))
4^(3^(2^1)) = 262144 ≠ 4096 = ((4^3)^2)^1
Additional sequence operations:
7
1. The sequence operations
> (define ls (list (list 1 2 3) (list 4 6 7) (list 8 9)))> (flatmap cdr ls)'(2 3 6 7 9)> (flatmap (lambda (x) (map sqr x)) ls)'(1 4 9 16 36 49 64 81)> (flatmap (lambda (lst) (filter odd? lst)) ls)'(1 3 7 9)
Signature: flatmap(proc, seq)Type:[[T1->List(T2)] * List(T1) -> List(T2)]Purpose: 1. Applies a function on every element of the given list, resulting in a new nested list. 2. Flattens the nested list (not recursively).
Question 1: flatmap
(define flatmap (lambda (proc seq) (accumulate append (list) (map proc seq))))
8
1. The sequence operations
Signature: flat-sum(ls)Type: [List -> Number]Precondition: ls is recursively made of lists and numbers only.Examples: (flat-sum '(1 2 3 (1 2 (1 (1 2 3 4) 2 3 (1 2 3 4) 4) 3 4) 4)) => 50
(flat-sum '(1 2 3 4)) => 10
Question 2: flat-sum
(define flat-sum (lambda (ls) (cond ((null? ls) 0) ((number? ls) ls) ((let ((car-sum (flat-sum (car ls))) (cdr-sum (flat-sum (cdr ls)))) (+ car-sum cdr-sum))))))
Implementation without sequence operations:
9
1. The sequence operations
Signature: flat-sum(ls)Type: [List -> Number]Precondition: ls is recursively made of lists and numbers only.Examples: (flat-sum '(1 2 3 (1 2 (1 (1 2 3 4) 2 3 (1 2 3 4) 4) 3 4) 4)) => 50
(flat-sum '(1 2 3 4)) => 10
Question 2: flat-sum
(define flat-sum (lambda (lst) (acc + 0 (map (lambda (x) (if (number? x) x (flat-sum x))) ls))))
Implementation with sequence operations:
Q: Could you think of a way to improve this Implementation?
10
1. The sequence operations
Signature: flat-sum(ls)Type: [List -> Number]Precondition: ls is recursively made of lists and numbers only.Examples: (flat-sum '(1 2 3 (1 2 (1 (1 2 3 4) 2 3 (1 2 3 4) 4) 3 4) 4)) => 50
(flat-sum '(1 2 3 4)) => 10
Question 2: flat-sum
(define flat-sum (let ((node->number (lambda (x) (if (number? x) x (flat-sum x))))) (lambda (lst) (acc + 0 (map node->number ls)))))
Implementation with sequence operations:
Q: How many closures are generated now?
11
1. The sequence operations
Signature: maximum(lst)Type: [List(Number)->Number]Pre-Condition: lst contains only natural numbers(define maximum (lambda (lst) (acc (lambda (a b) (if (> a b) a b)) 0 lst)))
Question 3: Sorting a list
Signature: insertion-sort(lst)Type: [List(Number)->List(Number)](define insertion-sort (lambda (lst) (if (null? lst) lst (let* ((max (maximum lst)) (rest (filter (lambda (x) (not (= max x))) lst))) (cons max (insertion-sort rest))))))
Implementation with sequence operations:
12
2. Partial evaluation with Currying
Signature: accumulate(op,initial,sequence)Purpose: Accumulate by ’op’ all sequence elements, starting (ending) with ’initial’Type: [[T1*T2 -> T2]*T2*LIST(T1) -> T2](define accumulate (lambda (op initial sequence) (if (null? sequence) initial (op (car sequence) (accumulate op initial (cdr sequence))))))
Question 4: Currying a recursive function - accumulate
Recall the implementation of accumulate:
Q: How would you Curry the procedure accumulate, delaying the sequence parameter?
13
2. Partial evaluation with Currying
Signature: c-accumulate-naive (op,initial)Type: [[[T*T->T]*T ->[List(T)->T]]Purpose: Partial application of accumulate (by operator and initial)(define c-accumulate-naive (lambda (op initial) (lambda (sequence) (if (null? sequence) initial (op (car sequence) ((c-accumulate-naive op initial) (cdr sequence)))))))
Question 4: Currying a recursive function - accumulate
Naïve Currying of accumulate:
14
2. Partial evaluation with Currying
Signature: c-accumulate-op-initial (op,initial)Type:[[[T*T->T]*T ->[List(T)->T]]Purpose: Partial application of accumulate (by operator and initial)(define c-accumulate-op-initial(lambda (op initial) (letrec ((iter (lambda (sequence) (if (null? sequence) initial (op (car sequence) (iter (cdr sequence))))))) iter)))
Question 4: Currying a recursive function - accumulate
A better version:
>(define add-accum (c-accumulate-op-initial + 0))>(add-accum '(1 2 3))6>(add-accum '(4 5 6))15
15
2. Partial evaluation with Currying
Signature: c-accumulate-sequence(seq)Type: [List(T)-> [[T*T->T]*T->T ]Purpose: Curried version of accumulate (by sequence)(define c-accumulate-sequence (lambda (sequence) (if (null? sequence) (lambda (op initial) initial) (let ((rest (c-accumulate-sequence (cdr sequence)))) (lambda (op initial) (op (car sequence) (rest op initial)))))))
Question 4: Currying a recursive function - accumulate
Currying by the sequence parameter:
>(define lst-accum (c-accumulate-sequence '(1 2 3)))>(lst-accum + 0)6>(lst-accum * 1)6
16
2. Partial evaluation with Currying
Signature: dists_k(ls,k)Type: [List(Number*Number) -> List(Number)]Examples: (dists_k (list (cons 3 4) (cons 3 7) (cons 15 12) (cons 3 12)) 3) => '(5 7.615773105863909 12.36931687685298)
Question 5: Using filter an map
Implement the procedure dists_k:
(define dists_k (lambda (ls k) (if (null? ls) ls (let ((xcord (caar ls)) (ycord (cdar ls)) (rest (dists_k (cdr ls) k))) (if (= xcord k) (cons (dist xcord ycord) rest) rest)))))
Implementation without sequence operations:
17
2. Partial evaluation with Currying
Signature: dists_k(ls,k)Type: [List(Number*Number) -> List(Number)]Examples: (dists_k (list (cons 3 4) (cons 3 7) (cons 15 12) (cons 3 12)) 3) => '(5 7.615773105863909 12.36931687685298)
Question 5: Using filter an map
Implement the procedure dists_k:
(define dists_k (lambda (ls k) (map (lambda (x) (dist (car x) (cdr x))) (filter (lambda (x) (= (car x) k)) ls))))
Implementation with sequence operations:
Q: Could you think of a way to improve this Implementation?
A list of pairs withX-coordinate equal to k.
18
2. Partial evaluation with Currying
Question 5: Using filter an map
(define dist (lambda (x y) (sqrt (+ (sqr x) (sqr y)))))
Examine the procedure dist:
(define dists_k (lambda (ls k) (map (lambda (x) (dist (car x) (cdr x))) (filter (lambda (x) (= (car x) k)) ls))))
A list of pairs withX-coordinate equal to k.
19
2. Partial evaluation with Currying
Question 5: Using filter an map
(define dist (lambda (x y) (sqrt (+ (sqr x) (sqr y)))))
Examine the procedure dist:
(define dists_k (lambda (ls k) (map (lambda (x) (dist (car x) (cdr x))) (filter (lambda (x) (= (car x) k)) ls))))
A list of pairs withX-coordinate equal to k.
(define c_dist (lambda (x) (let ((xx (sqr x))) (lambda (y) (sqrt (+ (sqr y) xx))))))
A curried version of the procedure dist:
20
2. Partial evaluation with Currying
Question 5: Using filter an map
(define dists_k (lambda (ls k) (map (lambda (x) (dist (car x) (cdr x))) (filter (lambda (x) (= (car x) k)) ls))))
A list of pairs withX-coordinate equal to k.
(define dists_k (lambda (ls k) (let ((dist_k (c_dist k))) (map (lambda (x) (dist_k (cdr x))) (filter (lambda (x) (= (car x) k)) ls)))))
New version of dists_k:
21
3. Lazy lists
Some reminders:
1. Lazy-lsits are serial data-structures that allow delayed computation.2. There is no need to store the entire list elements in memory.3. Lazy-lists may represent infinite series.4. The type of Lazy-lists is defined recursively:
LazyList = {()} union T*[Empty->LazyList]
Signature: cons(head, tail)Type: [T * [Empty->LazyList] -> LazyList]Purpose: Value constructor for lazy lists
Signature: head(lzl)Type:[LazyList -> T]Purpose: Selector for the first item of lzl (a lazy list)Pre-condition: lzl is not empty
Signature: tail(lzl)Type: [LazyList -> LazyList]Purpose: Selector for the tail (all but the first item) of lzl (a lazy list)Pre-condition: lzl is not empty
22
3. Lazy lists
Some reminders:
1. Lazy-lsits are serial data-structures that allow delayed computation.2. There is no need to store the entire list elements in memory.3. Lazy-lists may represent infinite series.4. The type of Lazy-lists is defined recursively:
LazyList = {()} union T*[Empty->LazyList]
Signature: take(lzl,n)Type: [LazyList * Number-> List]Purpose: Creates a List out of the first n items of lzl (a lazy list)Pre-condition: lzl length is at least n, n is a natural numberPost-condition: result[i]=lzl[i] for any i in range 0::n-1
Signature: nth(lzl,n)Type: [LazyList * Number-> T]Purpose: Gets the n-th item of lzl (a lazy list)Pre-condition: lzl length is at least n , n is a natural numberPost-condition: result=lzl[n-1]
23
3. Lazy lists
Examples:
Signature: integers-from(low)Purpose: Create a lazy-list containing all integers bigger than n-1 by their order.Pre-condition: n is an integer(define integers-from (lambda (n) (cons n (lambda () (integers-from (+ 1 n))))))
>(head (integers-from 5))5>(head (tail (integers-from 5)))6
24
3. Lazy lists
(define seven-boom! (lambda (n) (cons (cond ((= (modulo n 7) 0) 'boom) ((has-digit? n 7) 'boom) ((= (modulo (sum-digits? n) 7) 0) 'boom) (else n)) (lambda () (seven-boom! (+ n 1))))))
Question 6: 7-Boom
>(seven-boom! 1)‘(1 . #<procedure>)
>(take (seven-boom! 1) 7)‘(1 2 3 4 5 6 boom)
25
3. Lazy lists
Let
Question 7: Collatz conjecture
For every n>1, the sequence of numbers (n,f(f),f(f(n)),…) eventually reaches 1. For example: 563 -> 1690 -> 845 -> … 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1
(define lzl-collatz (lambda (n) (if (< n 2) (cons n (lambda () (list))) (cons n (lambda () (if (= (modulo n 2) 0) (lzl-collatz (/ n 2)) (lzl-collatz (+ (* 3 n) 1 ))))))))
> (take (lzl-collatz 563) 44)'(563 1690 845 ... 10 5 16 8 4 2 1)
26
3. Lazy lists
Question 8: lzl-filter
Signature: lzl-filter(pred lzl)Type: [[T -> Bool] * LazyList-> LazyList]Purpose: Creates a lazy list containing all members of lzl that fit the criteria pred(define lzl-filter (lambda (pred lzl) (cond ((null? lzl) lzl) ((pred (head lzl)) (cons (head lzl) (lambda () (lzl-filter pred (tail lzl))))) (else (lzl-filter pred (tail lzl))))))
> (lzl-filter (lambda (n) (= (modulo n 3) 0)) (integers-from 0))
Q: How could we generate the lazy-list of all integers that are divisible by 3?
27
3. Lazy lists
Question 8: Lazy list of prime numbers
(define primes (cons 2 (lambda () (lzl-filter prime? (integers-from 3)))))
> (take primes 6)’(2 3 5 7 11 13)
(define prime? (lambda (n) (letrec ((iter (lambda (lz) (cond ((> (sqr (head lz)) n) #t) ((divisible? n (head lz)) #f) (else (iter (tail lz))))) )) (iter primes)) ))