<<

in Scheme and Lisp

http://www.lisperati.com/landoflisp/

Overview • Remember: Lisp code is just an s-expression • In a functional , functions are first class objects • You can call Lisp’s evaluation process with the eval • You can create them, put them in data structures, compose them, specialize them, > (define s (list 'cadr ' ' (one two three))) apply them to arguments, etc. > s (cadr ' (one two three)) • We’ll look at how functional programming things are done in Lisp > (eval s) two > (eval (list 'cdr (car '((quote (a . b)) c)))) b

apply lambda • apply takes a function & a list of arguments for it & returns the result of applying the function to them • The define special form creates a function and > (apply + ' (1 2 3)) gives it a name 6 • However, functions don’t have to have names, • It can be given any number of arguments, so long as and we don’t need to use define to create the last is a list: them > (apply + 1 2 ' (3 4 5)) • The primitive way to create functions is to use 15 the lambda special form • A simple version of apply could be written as (define (apply f list) (eval (cons f list))) • These are often called lambda expressions, e.g. (lambda (x) (+ x 1))

1 lambda expression Lambda expression • lambda is a special form • A lambda expression is a list of the symbol • When evaluated, it creates a function and lambda, followed by a list of parameters, fol- returns a reference to it lowed by a body of one or more expressions: • The function does not have a name > (define f (lambda (x) (+ x 2))) • A lambda expression can be the first ele- > f ment of a function call: # > ( (lambda (x) (+ x 100)) 1) > (f 100) 101 • Other languages like python and 102 have adopted the idea > ( (lambda (x) (+ x 2)) 100) 102

define vs. define Functions as objects

(define (add2 x) • The define special form • While many PLs allow functions as (+ x 2) ) comes in two varieties arguments, nameless lambda functions • The three expressions to add flexibility (define add2 the right are entirely (lambda (x) (+ x 2))) equivalent > (sort '((a 100)(b 10)(c 50))

• The first define form is (lambda (x y) (< (second x) (second y)))) (define add2 #f) just more familiar and ((b 10) (c 50) (a 100)) convenient when defining (set! add2 • There is no need to give the function a (lambda (x) (+ x 2))) a function name

lambdas in other languages Mapping functions • Lisp & Scheme have several mapping functions • Lambda expressions are found in many • map (mapcar in Lisp) is the most useful modern languages, e.g., Python: • It takes a function and ≥1 lists and returns a list >>> f = lambda x,y: x*x + y of the results of applying the function to elements taken from each list >>> f > (map abs '(3 -4 2 -5 -6)) at 0x10048a230> (3 4 2 5 6) >>> f(2, 3) > (map + ‘(1 2 3) (4 5 6)) 7 (5 7 9) >>> (lambda x,y: x*x+y)(2,3) > (map + ‘(1 2 3) ‘(4 5 6) ‘(7 8 9)) 7 (12 15 18)

2 More map examples Defining map

> (map cons '(a b c) '(1 2 3)) Defining a simple “one argument” version of ((a . 1) (b . 2) (c . 3)) map is easy > (map (lambda (x) (+ x 10)) ‘(1 2 3)) (define (map1 func list) (11 12 13) (if (null? list) > (map + '(1 2 3) '(4 5)) null map: all lists must have same size; arguments were: # (1 2 3) (4 5) (cons (func (first list)) === context === /Applications/PLT/collects/scheme/private/misc.ss:7 (map1 func (rest list))))) 4:7

Define Lisp’s every and some Defining every is easy • every and some take a predicate and one or more sequences (define (every1 f list) • When given just one sequence, they test whether the elements satisfy the predicate ;; note the use of the and function > (every odd? ‘(1 3 5)) (if (null? list) #t > (some even? ‘(1 2 3)) #t #t • If given >1 sequences, the predicate takes as (and (f (first list)) many args as there are sequences and args (every1 f (rest list))))) are drawn one at a time from them: > (every > ‘(1 3 5) ‘(0 2 4)) #t

Define some similarly Will this work?

(define (some1 f list) • You can prove that P is true for some list ele- ment by showing that it isn’t false for every one (if (null? list) • Will this work? #f > (define (some1 f list) (or (f (first list)) (not (every1 (lambda (x) (not (f x))) list))) > (some1 odd? '(2 4 6 7 8)) (some1 f (rest list))))) #t > (some1 (lambda (x) (> x 10)) '(4 8 10 12)) #t

3 filter Example: filter

(filter ) returns a list of the elements of (define (filter1 func list) which satisfy the predicate ;; returns a list of elements of list where funct is true (cond ((null? list) null) > (filter odd? ‘(0 1 2 3 4 5)) ((func (first list)) (1 3 5) (cons (first list) (filter1 func (rest list)))) > (filter (lambda (x) (> x 98.6)) (#t (filter1 func (rest list))))) ‘(101.1 98.6 98.1 99.4 102.2)) (101.1 99.4 102.2) > (filter1 even? ‘(1 2 3 4 5 6 7)) (2 4 6)

Example: filter Here’s another pattern • Define integers as a function that returns a list of integers between a min and max • We often want to do something like sum the (define (integers min max) elements of a sequence (if (> min max) (define (sum-list l) null (if (null? l) 0 (cons min (integers (add1 min) max)))) (+ (first l) (sum-list (rest l))))) • Do prime? as a predicate that is true of • Other times we want their product prime numbers and false otherwise (define (multiply-list l) > (filter prime? (integers 2 20) ) (if (null? l) (2 3 5 7 11 13 17 19) 1 (* (first l) (multiply-list (rest l)))))

Here’s another pattern Example: reduce • We often want to do something like sum the • Reduce takes (i) a function, (ii) a final value elements of a sequence and (iii) a list of arguments (define (sum-list l) Reduce of +, 0, (v1 v2 v3 … vn) is just (if (null? l) 0 V1 + V2 + V3 + … Vn + 0 (+ (first l) (sum-list (rest l))))) • In Scheme/Lisp notation: • Other times we want their product > (reduce + 0 ‘(1 2 3 4 5)) (define (multiply-list l) 15 (if (null? l) 1 (reduce * 1 ‘(1 2 3 4 5)) (* (first l) (multiply-list (rest l))))) 120

4 Example: reduce (define (sum-list list) Using reduce ;; returns the sum of the list elements (define (reduce function final list) (reduce + 0 list)) (if (null? list) (define (mul-list list) ;; returns the sum of the list elements final (reduce * 1 list)) (function (define (copy-list list) (first list) ;; copies the top level of a list (reduce function final (rest list))))) (reduce cons ‘() list)) (define (append-list list) ;; appends all of the sublists in a list (reduce append ‘() list))

The roots of mapReduce Function composition

• MapReduce is a software frame- • Math notation: gh is a composition of func- work developed by Google for tions g and h parallel computation on large datasets on computer clusters • If f=gh then f(x)=g(h(x) • It’s become an important way to • Composing functions is easy in Scheme exploit parallel computing using > compose > (define sd (compose sq conventional programming languages and techniques. # dub)) > (define (sq x) (* x x)) > (sd 10) • See Apache’s Hadoop for an > (define (dub x) (* x 2)) 400 open source version > (sq (dub 10)) > ((compose dub sq) 10) 400 200 • The framework was inspired by functional > (dub (sq 10)) programming’s map, reduce & side-effect free programs 200

Defining compose Functions with any number of args

• Here’s compose for two functions in Scheme • Defining functions that takes any number of (define (compose2 f g) (lambda (x) (f (g x)))) arguments is easy in Scheme • Note that compose calls lambda which returns (define (foo . args) (printf "My args: ~a\n" a new function that applies f to the result of args))) applying g to x • If the parameter list ends in a symbol as • We’ll look at how the variable environments opposed to null (cf. dotted pair), then it’s value work to support this in the next topic, closures is the list of the remaining arguments’ values • But first, let’s see how to define a general ver- (define (f x y . more-args) …) sion of compose taking any number of args (define (map f . lists) … )

5 Compose in Scheme A general every (define (compose . FS) ;; Returns the identity function if no args given • We can easily re-define other functions to take (if (null? FS) more than one argument (lambda (x) x) (define (every fn . args) (lambda (x) ((first FS) ((apply compose (rest FS)) x))))) (cond ((null? args) #f) ; examples ((null? (first args)) #t) (define (add-a-bang str) (string-append str "!")) ((apply fn (map first args)) (define givebang (compose string->symbol add-a-bang symbol->string)) (apply every fn (map rest args))) (givebang 'set) ; ===> set! (else #f))) ; anonymous composition • (every > ‘(1 2 3) ‘(0 2 3)) => #t ((compose sqrt negate square) 5) ; ===> 0+5i • (every > ‘(1 2 3) ‘(0 20 3)) => #f

Functional Programming Summary

• Lisp is the archetypal functional programming language • It treated functions as first-class objects and uses the same representation for data & code • The FP paradigm is a good match for many problems, esp. ones involving reasoning about or optimizing code or parallel execution • While no pure FP languages are considered mainstream, many PLs support a FP style

6