390 likes | 519 Views
Functional Programming. Universitatea Politehnica Bucuresti 2007-2008 Adina Magda Florea http://turing.cs.pub.ro/fp_08. Lecture No. 2. S-expressions Data types Defining variables and procedures Special forms Local variables Control constructs Objects and pointers. 1. S-expressions.
E N D
Functional Programming Universitatea Politehnica Bucuresti2007-2008 Adina Magda Florea http://turing.cs.pub.ro/fp_08
Lecture No. 2 • S-expressions • Data types • Defining variables and procedures • Special forms • Local variables • Control constructs • Objects and pointers
1. S-expressions • In Scheme, there's no distinction between expressions and statements • They're all "expressions" – s-expressions • Scheme expressions combine the features of expressions and statements. • S-expressions return values • However, they can also have side effects
2. Data types in Scheme • Simple types: booleans, numbers, characters, and symbols Booleans • #t for true and #f or () for false • boolean? - checks if its argument is boolean Numbers • integers (eg, 42), rationals (22/7), reals (3.1416), or complex (2+3i) • Predicates: • number?complex?real?rational?integer? • eqv? • =, <, <=, >, >= self-evaluating
Simple types: Characters Characters • Character data - represented by prefixing the character with #\ char? (char? #\c) => #t (char? 1) => #f (char? #\;) => #t char=?, char<?, char<=?, char>?, char>=? (char=? #\a #\a) => #t (char<? #\a #\b) => #t (char>=? #\a #\b) => #f • To make the comparisons case-insensitive, use char-ci (=?...) self-evaluating
Simple types: Symbols Symbols (symbol? 'xyz) => #t (symbol? 42) => #f (eqv?'Calorie 'calorie) => #t (define xyz 9) xyz => 9 • We can use the form set! (pronounced "set-bang") to change the value held by a variable: (set! xyz #\c) xyz => #\c • - are identifiers • are evaluated • (unless quote is used) mutation procedure Note on type You should not use assignments a lot in Scheme programs. It's usually a sign of bad style
Structured types: Strings Strings "Hello, World!" => "Hello, World!" (string #\h #\e #\l #\l #\o) => "hello" (define greeting "Hello; Hello!") (string-ref greeting 0) => #\H • New strings can be created by appending other strings: (string-append "E " "Pluribus " "Unum") => "E Pluribus Unum"
Structured types: Strings • Make a string of a specified length, and fill it with the desired characters later. (define a-3-char-long-string (make-string 3)) • string? • Strings obtained as a result of calls to string, make-string, and string-append are mutable. (define hello (string #\H #\e #\l #\l #\o)) hello => "Hello" (string-set! hello 1 #\a) hello => "Hallo"
Structured types: Vectors Vectors (vector 0 1 2 3 4) => #(0 1 2 3 4) (define v (make-vector 5)) (set! v #(1 2 3 4 6)) v => #5(1 2 3 4 6) (vector? #(1 2 3)) => #t (vector-ref #(1 2 3) 0) => 1 (vector-ref #(1 2 3) 3) => vector-ref: index 3 out of range [0, 2] for vector: #3(1 2 3) (vector-set! v 1 10) v => #5(1 10 3 4 6)
Structured types: Dotted pairs • A dotted pair is a compound value made by combining any two arbitrary values into an ordered couple. (cons 1 #t) => (1 . #t) (define x (cons 1 #t)) (car x) => 1 (cdr x) => #t (set-car! x 2) (set-cdr! x #f) x => (2 . #f) (define y (cons (cons 1 2) 3)) y => ((1 . 2) . 3) (car (car y)) => 1 (cdr (car y)) => 2 (caar y) => 1 (cdar y) => 2
Structured types: Lists • The abbreviation for a dotted pair of the form (1 . (2 . (3 . (4 . ())))) is (1 2 3 4) • This special kind of nested dotted pair is called a list • (cons 1 (cons 2 (cons 3 (cons 4 '())))) (list 1 2 3 4) => (1 2 3 4) (define y (list 1 2 3 4)) (list-ref y 0) => 1 (list-ref y 3) => 4 (list-tail y 1) => (2 3 4) (list-tail y 3) => (4)
Structured types: Lists • The predicates pair?, list?, and null? check if their argument is a dotted pair, list, or the empty list, (pair? '(1 . 2)) => #t (pair? '(1 2)) => #t (pair? '()) => #f (list? '()) => #t (null? '()) => #t (list? '(1 2)) => #t (list? '(1 . 2)) => #f (null? '(1 2)) => #f (null? '(1 . 2)) => #f
Structured types: Lists • List of pairs: • car field hold the pointers to the object • cdr field link the pairs together into a "spine." • A list is really just a sequence of pairs, ending with a null pointer. • A null pointer is a list, too - it's a sequence of zero pairs ending in a null pointer. • An object can easily be in many lists at once, because a list is really just a spine of pairs that holds pointers to the items in the list. foo pair pair pair 22 13 15 bar pair pair
Lists and quote • quote takes exactly one argument, and returns a data structure whose printed representation is the same as what you typed in as the argument to quote. • Scheme does not evaluate the argument to quote as an expression - it just gives you a pointer to a data structure. • For example, the expression (define foo (quote (1 2 3))) defines (and binds) a variable foo, and initializes its binding with (a pointer to) a three-element list.
Lists and quote (define (foo) '(1 2 3)) • The list (1 2 3) may be created when we define the procedure foo, and each time we call it, it may return a pointer to that same list. • For this reason, it's an error to modify a data structure returned from a quote form. • If we want the procedure foo to return a new list (1 2 3) every time, we can write (define (foo) (list 1 2 3))
Some list procedures length (define (my-length lis) (cond ((null? lis) 0) (else (+ 1 (my-length (cdr lis)))))) append (define (my-append lis1 lis2) (cond ((null? lis1) lis2) (else (cons (car lis1) (my-append (cdr lis1) lis2))))) (my-append '(a b c) '(x y)) => (a b c x y) (my-append '((a b) c (d e)) '(x (y z) t)) => ((a b) c (d e) x (y z) t)
Some list procedures • appendconcatenates the lists it is given. • It only concatenates the top-level structure, however - it doesn't "flatten" nested structures. • append doesn't modify any of its arguments, but the result of append generally shares structure with the last list it's given. • It effectively conses the elements of the other lists onto the last list to create the result list. • It's therefore dangerous to make a "new" list with append and then modify the "old" list. • This is one of the reasons side effects are discouraged in Scheme.
Some list procedures Copying lists There are two common senses of copying, shallow copying, and deep copying. Shallow copy (define (pair-copy pr) (cons (car pr) (cdr pr)))
Some list procedures Copying lists Deep copy (define (pair-tree-deep-copy thing) (if (not (pair? thing)) thing (cons (pair-tree-deep-copy (car thing)) (pair-tree-deep-copy (cdr thing)))))
Some list procedures Copying lists (define (list-copy lis) (cond ((null? lis) '()) (else (cons (car lis) (list-copy (cdr lis))))
Structured types: Procedures Primitives and procedures • The variable denoting a Scheme primitive or a defined procedure holds that procedure cons => #<primitive:cons> car => #<primitive:car> list-sum => #<procedure:list-sum> • When you write a procedure that modifies its arguments, rather than just returning a value, it's good style to give it a name that ends with ! • e.g. reverse vs reverse!
Non-distructive reverse (define (my-reverse lis) (cond ((null? lis) ()) (else (append (my-reverse (cdr lis)) (list (car lis)))))) (define m '(a b c)) (my-reverse m) => (c b a) m => (a b c)
Distructive reverse ; returns the last element of a list ; to be used in my-reverse! (define (all-but-last lis) (cond ((null? lis) ()) ((null? (cdr lis)) ()) (else (cons (car lis) (all-but-last (cdr lis)))))) ;my-reverse! distroys the initial list by reversing it (define (my-reverse! lis) (let ((l ()) (len (length lis))) (cond ((null? lis) ()) (else (set! l (list-ref lis (- len 1))) (set-cdr! lis (my-reverse! (all-but-last lis))) (set-car! lis l) lis)))) (all-but-last '(a b c)) => (a b) (define m '(a b c)) (my-reverse! m) => (c b a) m => (c b a)
Mind that … ; my-rev! seems to destroy the initial list but in fact it does not (define (my-rev! lis) (cond ((null? lis) ()) (else (set! lis (append (my-rev! (cdr lis)) (list (car lis)))) lis))) (define m '(a b c)) (my-rev! m) => (c b a) m => (a b c)
Ports • Yet another data type is the port. • A port is the conduit through which input and output is performed. • Ports are usually associated with files and consoles.
3. Defining variables and procedures Defining a variable : • (define my-variable 5) - tells Scheme to allocate space for my-variable, and initialize that storage with the value 5. • In Scheme, you always give a variable an initial value, so there's no such thing as an uninitialized variable or an unininitialized variable error. • Scheme values are always pointers to objects • e.g., when we use the literal 5, Scheme interprets that as meaning a pointer to the object 5. • Numbers are objects you can have pointers to, just like any other kind of data structure.
Defining variables • The define expression does three things: • It declares to Scheme that we're going to have a variable named foo in the current scope. • It tells Scheme to actually allocate storage for the variable. The storage is called a binding. • It tells Scheme what initial value to put in the storage. • You can not use set! on a variable that has not been defined
Defining procedures Defining a procedure: • Use the special form lambda to defined an un-named procedure (lambda (x) (+ x 2)) ((lambda (x) (+ x 2)) 5) => 7 • Named procedures: use a variable to hold the procedure value: (define add2 (lambda (x) (+ x 2))) (add2 4) => 6 (add2 9) => 11
Defining procedures Defining a procedure: • Or just use define and indicate name and parameters • (define (two-times x) (+ x x)) • When you define a procedure, you're really defining a variable whose value happens to be a (pointer to a) procedure. • You can define a procedure with 0 parameters • (define (foo) 15) • Mind the difference (define foo 15)
Defining procedures Variable number of arguments: • Some procedures can be called at different times with different numbers of arguments. • To do this, the lambda parameter list is replaced by a single symbol. • This symbol acts as a variable that is bound to the list of the arguments that the procedure is called on. • The lambda parameter list can be: • a list of the form (x ...) • a symbol • a dotted pair of the form (x ... . z); in the dotted-pair case, all the variables before the dot are bound to the corresponding arguments in the procedure call, with the single variable after the dot picking up all the remaining arguments as one list.
4. Special forms Special forms: • A kind of procedure but behaves differently • Procedure calls and special forms are syntactically similar but semantically different • Examples: • set! - isn't a procedure, because its first argument is not really an expression to be evaluated in the normal way, to get a value to pass as an argument. It's the name of a place to put a value • define treats its first argument specially--the name of a variable or procedure isn't an expression that is evaluated and passed to define - it's just a name, and you're telling define to allocate some storage and use that name for it.
Special forms • Other special forms we'll see include • control constructs: if, cond, and case, etc., and logical operators and and or; • forms for defining local variables: let and its variants letrec and let*; • looping constructs: named let and do; • quote, which let you write complex data structures as textual literals in your code, and • lambda, which creates new procedures
5. Local variables • forms for defining local variables: let and its variants letrec and let*;
6. Control constructs • control constructs: if, cond, and case, etc., and logical operators and and or;
7. Objects and pointers • Conceptually, all Scheme objects are allocated on the heap, and referred to via pointers. • A procedure takes pointers to the arguments and returns a pointer to the value computer and returned by the procedure • This makes things simpler
Objects and pointers • An implementation is free to optimize away the pointers if it doesn't affect the programmer's view of things • Most implementations actually do this
Objects and pointers • Objects on the heap
Objects and pointers • Reclaiming the memory
Objects and pointers • Dymanic typing