220 likes | 363 Views
159.331 Programming Languages & Algorithms. Lecture 21 - Functional Programming Languages - Part 3 Example Applications and Example Languages. Example Programs - Quicksort. Quicksort is an efficient algorithm for sorting a list of items (eg numbers)
E N D
159.331 Programming Languages & Algorithms Lecture 21 - Functional Programming Languages - Part 3 Example Applications and Example Languages Prog Lang & Alg
Example Programs - Quicksort • Quicksort is an efficient algorithm for sorting a list of items (eg numbers) • Quicksort determines the value of the first element in the list, say x. • Then it creates two sublists one with elements less than or equal to x and one with values greater than x • Each sublist is then sorted recursively (using quicksort) • Finally the the resulting lists and x are put together and returned as a result • Conceptually neat - a bit messy to code imperatively - compactly coded in eg Miranda Prog Lang & Alg
quicksort [ ] = [ ] || empty list is already sorted quicksort ( x: tail ) || general case = quicksort [a | a <- tail; a <= x ] || first sublist ++ [x] || head ++ quicksort [a | a <- tail; a > x] || second sublist • First equation is the trivial case (empty list) • Second is general case builds the two sublists using list comprehension • [a| a<-tail; a<= x] is list of all elements a of list tail that are less than or equal to x • Next invoke quicksort recursively for both lists • Results and the list [x] are put together using list concatenation operator ++ Prog Lang & Alg
quicksort function is polymorphic with type: quicksort :: [] -> [] • So it takes a list of any type and returns a list with the same type of elements • It can be invoked: quicksort [ 5, 2, 9, 1 ] => [ 1, 2, 5, 9 ] quicksort[ ‘d’, ‘a’, ‘b’, ‘c’] => [ ‘a’, ’b’, ’c’, ’d’ ] • Functional programming typically lets you code algorithms in a way that is close to their mathematical specification than imperative languages allow Prog Lang & Alg
Example Programs - Hamming • This problem requires we generate all numbers that can be written as: • 2^i * 3^j * 5^k • Such Hamming numbers contain only the factors 2, 3 and 5 • Output will start like: 1,2,3,4,5,6,8,9,10, 12,15,16,… • And will not finish until stopped by the user. • We want sorted output, with no duplicates. Prog Lang & Alg
3 nested imperative loops over i,j,k is intuitive solution, but does not address need for sorted non-duplicates - much harder! • We can solve in neat way by imagining 3 parallel processes doing a multiplication • each multiplier takes the same stream of input values and each produces a separate output • eg first multiplies its input by 2, second by 3, third by 5 • Each of the three output streams is already sorted • A fourth process merges them in increasing order, removing duplicates “on the fly” as it goes • The output is fed back into the input stream • A “1” on the input is used to kick things off Prog Lang & Alg
Input stream will contain exactly the Hamming numbers, in order and without duplicates Prog Lang & Alg
Miranda does not provide parallel processes but we can still implement using lazy evaluation: mul :: num -> [num] -> [num] || multiply elements in mul f s = [ f * x] x <- s ] || list by given factor ham :: [num] || generate Hamming Numbers ham = 1 : merge3 (mul 2 ham) (mul 3 ham ) (mul 5 ham) • A multiplier is obtain through function mul which multiplies all values on list s by a given factor f (2, 3 or 5) • Function ham builds a list containing the values 1 and the result of a 3-way merge of the three multiplier processes • Function ham has no arguments so just invoke with “ham” Prog Lang & Alg
We need a 3-way merge function • It can be built from a 2-way merge • (a 2-way merge is built in but we can write our own that removes duplicates as well) merge3 x y z = merge2 x (merge2 y z) merge2 (x:xs) (y:ys) || merge 2 sorted lists = (x:merge2 xs (y:ys)), if x<y = (x:merge2 xs ys ), if x = y || eliminate duplicates = (y:merge2 (x:xs) ys), if x>y • Relies on lazy evaluation (with strict semantics the first multiplier would get into an infinite loop. With lazy evaluation however the system first tries to reduce merge3 and only evaluates the arguments when needed ) Prog Lang & Alg
Functional Languages • Lisp and variants, Common Lisp, Scheme • ISWIM (attempt to get rid of all the Lisp parentheses) • FLPL - Fortran List processing language (!) • Hope, ML, Standard ML • Miranda, Haskell • Lucid, Id • Haskell perhaps the most useful at present… • (free compilers - eg HUGS, GHC,… lots of work and effort and applications) Prog Lang & Alg
Example Languages - Lisp • Designed by McCarthy, 1958, the first functional language • List processing language- lists are primary data structure • Many variants since: MacLisp; Common Lisp • Mainly designed for symbolic applications such as AI • Early implementations too slow, and often criticised because of the multiple parentheses that are so hard to read - eg the factorial example in Lisp looks like: (define (fac x) (cond ( (= x 0) 1) ( t (* x (fac (- x 1) ) ) ) ) ) • Syntax is uniform however, making it easy to manipulate Lisp programs as normal data Prog Lang & Alg
Supports recursion and higher-order functions, memory allocation done with automatic garbage collection • Lisp has no type system (allows lists of any elements) - hence hard to detect type-system errors at compile time • Lisp uses dynamic binding • Lisp is direct ancestor of lots of modern functional languages - most notably Scheme • Lisp’s successors typically have a type system & support features such as lazy evaluation and pattern matching • Lisp it turns out, does not derive from the Lambda calculus, but variants have been retro-fitted to make them close to it spirit • Emacs text editor is written in Lisp - Lisp useful for debuggers and other software and language tools Prog Lang & Alg
Example Languages - Scheme • From MIT in mid 1970s - Sussman and Steele • Small language • Exclusively static scoping • Functions are first-class entities • Scheme interpreter - read-evaluate-write infinite loop • EVAL function evaluates expressions - literals evaluate to themselves Prog Lang & Alg
Some Scheme Features • To avoid evaluating a parameter use the QUOTE function - abbreviated as ` • (QUOTE A) returns A • instead of (QUOTE (A,B) ) , write `(A,B) • (DISPLAY expression) and (NEWLINE) are useful for getting output • (+ 1 2 3 4 ) returns 10 • ( - 24 ( * 4 3 ) ) returns 12 • COND similar to ternary operator we saw before but generalised to allow more than one predicate to be true at once Prog Lang & Alg
COND in Scheme (COND (predicate_1 expression {expression} ) (predicate_2 expression {expression} ) …. (predicate_n expression {expression} ) (ELSE expression {expression} ) ) (We use the {} to denote an optional list of expressions ) A bit like a SWITCH/CASE - Predicates evaluated one at a time until one is true, then the expressions are evaluated and the last value is the return value from COND Prog Lang & Alg
COND Example in Scheme (DEFINE (compare x y ) (COND (( > x y) (DISPLAY “x is greater than y” )) ((< x y ) (DISPLAY “y is greater than x” )) (ELSE (DISPLAY “x and y are equal” )) ) Prog Lang & Alg
Scheme - factorial Example (DEFINE (factorial n) ( IF ( = n 0 ) 1 ( * n (factorial ( - n 1 ) ) ) ) ) Prog Lang & Alg
Scheme Primitives • CAR - head element of a list • CDR - tail - ie the rest of the list • CONS as : in Miranda • Historically these legacy names come from Lisp - and IBM704 machine instructions - “contents of address register” and “contents of decrement register” • Use `() for empty list NULL? predicate function tests for the empty list • (EQ? `A `B) returns #T, true if A and B are equal • LIST? predicate returns #T if its single arg is a list Prog Lang & Alg
Scheme - Example - equal function - on two lists (DEFINE (equal lis1 lis2) (COND ((NOT (LIST? lis1)) (EQ? lis1 lis2)) ((NOT (LIST? lis2)) `() ) ((NULL? lis1) (NULL? lis2)) ((NULL? lis2)) ` () ) ((equal (CAR lis1) (CAR lis2)) (equal (CDR lis1) (DR lis2))) (ELSE `() ) ) ) Prog Lang & Alg
Apply-to-All Function • Recall map function in Miranda- we have mapcar in Scheme (DEFINE (mapcar fun lis) (COND ( (NULL? Lis) `() ) (ELSE ( CONS (fun (CAR lis)) (mapcar fun (CDR lis)))) )) • Can use on the nameless cube function: (mapcar (LAMBDA (num) (* num num num) `(3 4 2 6) ) • This applies mapcar to a LAMBDA expression Prog Lang & Alg
More on Scheme • Sebesta Chapter 15, section 15.5 is on Scheme • “The Little Schemer” 4th Edition, MIT Press ISBN 0262560992 by Friedman and Felleisen is a useful textbook • MIT Scheme webpages (including Gnu Scheme): www.swiss.ai.mit.edu/projects/scheme/ • Scheme has some imperative features so it is a hybrid rather than a purely functional language • It is still a dialect of Lisp so has many of the features of Lisp Prog Lang & Alg
We looked at some examples in Miranda At some Lisp and Scheme ideas Bal & Grune Chapter 4 Sebesta Chapter 5 Next ML, SML, Haskell and Lambda Calculus… Summary Prog Lang & Alg