List Processing
- (b.t.w. LISP acronym stands for ~)
- lists
- quote; why it's needed; functor evaluation
- cadr, caddr, etc.
- first, last convenience functions
- cons
the "malloc" of lisp/scheme, like Java new() operator, except the (classes)
types in Lisp are simpler: built-in/atomic types and cons cells
takes exactly two arguments
- car
- cdr
- cons cells == pairs
- examples forming and accessing pairs (cons cells)
(define pair1 (cons 1 2))
(car pair1)
(cdr pair1)
(define two-cells (cons 1 (cons 2 nil)))
(define three (list 1 2 3))
(define (pair x y)
(cons x y) )
(define (getx aPair)
(car aPair) )
(define (gety aPair)
(cdr aPair) )
(define p (pair 3 7))
(getx p)
(gety p)
CAR CDR
+-----+-----+
| | |
| . | .---- 7
| | | |
+--|--+-----+
|
3
. ---- 7
|
3
We can build arbitrary structures with CONS cells;
but again, note that pairs are not lists.
It is customary to make lists using CONS cells; and there are certain
conventions used.
- car
the head of the list
the first element of the list
- cdr
the tail of the list, which is also (usually) a list
note, tail does not mean the same thing as last element of the list
(cons 1 (cons 2 (cons 3 nil)))
(cons 1 (cons 2 (cons 3 ())))
. ---- . ---- . ---- ()
| | |
a b c
> (null? ())
#t
> (null? null)
#t
> (null? (cons 1 2))
#f
> (null? 1)
#f
()
(a)
((a))
(((a b)))
(a . b)
((a b . c))
(a b . c)
(a b . nil)
(a b . (c))
(a b c . (c d))
(a b c (d) e)
> (list? 1)
#f
> (list? (cons 1 2))
#f
> (list? (cons 1 (cons 2 (cons 3 ()))))
#t
> (list? (cons 1 (cons 2 (cons 3 4))))
#f
> (list? (cons (cons 1 2) (cons (cons 3 4) ())))
#t
The special form, quote, prevents its argument from being evaluated.
> (* 1 2)
2
> (quote (* 1 2))
(* 1 2)
> (1 2)
procedure application: expected procedure, given: 1; arguments were: 2
> (quote (1 2))
(1 2)
'(* 1 2)
'(1 2)
(define a 1)
(define b 2)
(cons a b)
(cons 'a 'b)
(cons 'a 'c) ;; note c is an unbound variable, but OK here because its quoted
(cons 1 2)
(cons '1 '2)
> (list 3 7)
(3 7)
> (list (* 3 7))
(21)
> (list '(* 3 7))
((* 3 7))
> (list '(* 3 7) (* 4 8) '(5 9))
((* 3 7) 32 (5 9))
> (list '())
(())
> (list null)
(())
> (list? (* 3 7))
#f
> (list? '(* 3 7))
#t
(cons 1 (cons 2 (cons 3 (cons 4 ()))))
(cons 1 '(2 3 4))
When you want to write list expressions that are mostly literal but have some designated holes to be filled in with runtime values, use "quasiquote".
> (define a 4)
> (define b '(5 6))
> (quasiquote (1 2 3))
(1 2 3)
> (quasiquote (1 2 (unquote a) (unquote b) 7))
(1 2 4 (5 6) 7)
> (quasiquote (1 2 (unquote a) (unquote-splicing b)))
(1 2 4 5 6)
> `(1 2 ,a ,b 7)
(1 2 4 (5 6) 7)
> `(1 2 ,a ,@b 7)
(1 2 4 5 6 7)
Lists and recursion go together.
(define (element? x listarg)
(and (list? listarg)
(or (eq? x (car listarg))
(element? x (cdr listarg)) ) ) )
(element? 1 '(1 2 3))
(element? 3 '(1 2 3))
(element? 3 '())
(element? 3 'foobar)
Discussions re: read-eval-print, PLT scheme, tail recursion vs. while,
accumulators, difference lists.
Accumulators, difference lists.
CPS - continuation passing style
main idea: forces all calls to be tail recursive
if there is additional work to be done after the call,then instead rewrite so
as to pass the additional work onto the recusive call.
May require introducing extra loop variables (args for the recusive call)
Can always rewrite using CPS
tail recusion is easily optimized into while loop.
For comparison, here are some Ruby examples illustrating tail recursion:
int fact(int i) {
if (i == 1) then
return 1;
else
int tmp := fact(i-1);
tmp := tmp * i; // need to do multiplication - more work after call
return tmp;
endif
}
# Ruby solution using tail recursion.
def fact(i)
return fact_iter(i, 1);
end
def fact_iter(i, result)
if (i == 1)
return result
else
return fact_iter(i-1, result*i)
end
end
print fact(5)
#Ruby solution as a loop.
def fact_loop(i)
result = 1
while (i != 1)
i, result = i-1, result*i
end
return result
end
print fact_loop(5)
delay and force in Scheme
delay "promises" an expression to maybe evaluate later sometime
force evaluates and memoizes the result
Nice example from the text.
(define naturals
(letrec ((next (lambda (n) (cons n (delay (next (+1 n)))))))
(next 1)))
(define head car)
(define (tail stream)
(force (cdr stream)))
(define tail (lambda (stream) (force (cdr stream)))