Survey
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
Practice session #6:
1. Sequence operations
2. Partial evaluation with currying
3. Lazy-lists
1. The sequence operations
Motivation: The procedure signs maps a collection of numbers to their sign.
In C:
For (int i=0; i<arrayLength; i++) {
if (a[i] == 0) b[i] = 0 ;
else
b[i] = a[i]/abs(a[i]);
}
In Scheme:
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)))))))
2
1. The sequence operations
Motivation: The procedure signs maps a collection of numbers to their sign.
The sequence operation map:
Signature: map(proc,items)
Purpose: Apply 'proc' to all 'items'.
Type: [[T1 -> T2]*List(T1) -> List(T2)]
Implementation using sequence operations:
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)))
3
1. The sequence operations
Additional sequence operations:
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))
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.
4
1. The sequence operations
Additional 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))))
5
1. The sequence operations
Additional sequence operations:
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]))))
For example:
> (accumulate + 0 (list 1 2 3 4 5))
15
> (accumulate expt 1 (list 4 3 2))
262144
4^(3^(2^1)) = 262144 ≠ 4096 = ((4^3)^2)^1
6
1. The sequence operations
Question 1: flatmap
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).
> (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)
(define flatmap
(lambda (proc seq)
(accumulate append
(list)
(map proc seq))))
7
1. The sequence operations
Question 2: flat-sum
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
Implementation without sequence operations:
(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))))))
8
1. The sequence operations
Question 2: flat-sum
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
Implementation with sequence operations:
(define flat-sum
(lambda (lst)
Q: Could you think of a way to
(acc +
improve this Implementation?
0
(map (lambda (x)
(if (number? x)
x
(flat-sum x)))
ls))))
9
1. The sequence operations
Question 2: flat-sum
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
Implementation with sequence operations:
(define flat-sum
(let ((node->number (lambda (x)
(if (number? x)
x
(flat-sum x)))))
(lambda (lst)
(acc + 0 (map node->number ls)))))
Q: How many closures are generated now?
10
1. The sequence operations
Question 3: Sorting a list
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)))
Implementation with sequence operations:
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))))))
11
2. Partial evaluation with Currying
Question 4: Currying a recursive function - accumulate
Recall the implementation of 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]
(define accumulate
(lambda (op initial sequence)
(if (null? sequence)
initial
(op (car sequence)
(accumulate op initial (cdr sequence))))))
Q: How would you Curry the procedure accumulate, delaying the sequence parameter?
12
2. Partial evaluation with Currying
Question 4: Currying a recursive function - accumulate
Naïve Currying of accumulate:
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)))))))
13
2. Partial evaluation with Currying
Question 4: Currying a recursive function - accumulate
A better version:
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)))
>(define add-accum (c-accumulate-op-initial + 0))
>(add-accum '(1 2 3))
6
>(add-accum '(4 5 6))
15
14
2. Partial evaluation with Currying
Question 4: Currying a recursive function - accumulate
Currying by the sequence parameter:
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)))))))
>(define lst-accum (c-accumulate-sequence '(1 2 3)))
>(lst-accum + 0)
6
>(lst-accum * 1)
6
15
2. Partial evaluation with Currying
Question 5: Using filter an map
Implement the procedure dists_k:
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)
Implementation without sequence operations:
(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)))))
16
2. Partial evaluation with Currying
Question 5: Using filter an map
Implement the procedure dists_k:
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)
Implementation with sequence operations:
(define dists_k
(lambda (ls k)
(map (lambda (x) (dist (car x) (cdr x)))
(filter (lambda (x) (= (car x) k))
ls))))
Q: Could you think of a way to improve this Implementation?
17
A list of pairs with
X-coordinate equal to k.
2. Partial evaluation with Currying
Question 5: Using filter an map
Examine the procedure dist:
(define dist (lambda (x y) (sqrt (+ (sqr x) (sqr y)))))
(define dists_k
(lambda (ls k)
(map (lambda (x) (dist (car x) (cdr x)))
(filter (lambda (x) (= (car x) k))
ls))))
18
A list of pairs with
X-coordinate equal to k.
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 with
X-coordinate equal to k.
Examine the procedure dist:
(define dist (lambda (x y) (sqrt (+ (sqr x) (sqr y)))))
A curried version of the procedure dist:
(define c_dist
(lambda (x)
(let ((xx (sqr x)))
(lambda (y)
(sqrt (+ (sqr y) xx))))))
19
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))))
New version of dists_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)))))
20
A list of pairs with
X-coordinate equal to k.
3. Lazy lists
Some reminders:
1.
2.
3.
4.
Lazy-lsits are serial data-structures that allow delayed computation.
There is no need to store the entire list elements in memory.
Lazy-lists may represent infinite series.
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
21
3. Lazy lists
Some reminders:
1.
2.
3.
4.
Lazy-lsits are serial data-structures that allow delayed computation.
There is no need to store the entire list elements in memory.
Lazy-lists may represent infinite series.
The type of Lazy-lists is defined recursively:
LazyList = {()} union T*[Empty->LazyList]
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 number
Post-condition: result=lzl[n-1]
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 number
Post-condition: result[i]=lzl[i] for any i in range 0::n-1
22
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
23
3. Lazy lists
Question 6: 7-Boom
(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))))))
>(seven-boom! 1)
‘(1 . #<procedure>)
>(take (seven-boom! 1) 7)
‘(1 2 3 4 5 6 boom)
24
3. Lazy lists
Question 7: Collatz conjecture
Let 𝑓 𝑛 =
𝑛\2,
𝑖𝑓 𝑛 𝑖𝑠 𝑒𝑣𝑒𝑛
3𝑛 + 1, 𝑖𝑓 𝑛 𝑖𝑠 𝑜𝑑𝑑
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)
25
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))))))
Q: How could we generate the lazy-list of all integers that are divisible by 3?
> (lzl-filter
(lambda (n) (= (modulo n 3) 0))
(integers-from 0))
26
3. Lazy lists
Question 8: Lazy list of prime numbers
(define primes
(cons 2 (lambda () (lzl-filter prime? (integers-from 3)))))
(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))
))
> (take primes 6)
’(2 3 5 7 11 13)
27
3. Lazy lists
Question 8: Lazy list of prime numbers
(define sieve
(lambda (lz)
(cons (head lz)
(lambda ()
(sieve (lzl-filter
(lambda (x) (not (divisible? x (head lz))))
(tail lz)))))
))
(define primes1 (sieve (integers-from 2)))
> (take primes1 7)
’(2 3 5 7 11 13 17)
28