33
Scheme as implemented by Racket (Simple view:) Racket is a version of Scheme. (Full view:) Racket is a platform for implementing and using many languages, and Scheme is one of those that come out of the box. Racket’s version of Scheme is somewhat different from the standards, e.g., function names, some features. My slides are right for Racket, but may fail with standard Scheme. But same principles. My slides give you a taste, but there are a lot of useful things I won’t cover. 1 / 33

Scheme as implemented by Racket - University of Torontotrebla/CSCC24-2018-Winter/01-scheme-p1.pdf · Scheme as implemented by Racket ... Anonymous Functions/Procedures (lambda, )

Embed Size (px)

Citation preview

Scheme as implemented by Racket(Simple view:) Racket is a version of Scheme.

(Full view:) Racket is a platform for implementing and using manylanguages, and Scheme is one of those that come out of the box.

Racket’s version of Scheme is somewhat different from thestandards, e.g., function names, some features. My slides are rightfor Racket, but may fail with standard Scheme.

But same principles.

My slides give you a taste, but there are a lot of useful things Iwon’t cover.

1 / 33

Basic Data TypesAnd Syntax of Their Literals

I Booleans: #t, #f

I Numbers: 42 (integers), 1/3 (rational numbers), 42.5(floating-point numbers), 3+4i (complex numbers)

I Strings: "hello"

Single characters: #\h stands for the letter h.

I Symbols: User-defined atomic values. You think up a name,put a single-quote in front. Four examples:’Firefox ’Chrome ’Edge ’Safari

Great for tags, labels, enumerations.

Symbols are not strings. You can’t append, split, ask for thenth character. . .

2 / 33

Expressions

I Literals: Examples on the previous slide.

I Identifiers: E.g., +, my-angle/time

I Procedure/Function Applications: (proc param1 param2 ...)

proc and parameters are expressions. proc should evaluateto a procedure.

E.g., (sin (/ 0.2 2))

E.g., ((if #t sin cos) (/ 0.2 2))

I Special Forms: (keyword expr expr ...)

For definitions, conditionals, other features. . . They have theirown slides.

3 / 33

Some Boolean Operations(not expr)

(and expr expr ...)

Short-circuits.(and) gives #t.

(or expr expr ...)

Short-circuits.(or) gives #f.

(boolean? expr)

Tests whether you have a boolean value.

4 / 33

Some Number Operations+ - * / max min can take multiple operands.

quotient remainder abs sqrt sin cos tan etc.

(expt b x) means bx.(exp x) means ex.(log x) means ln x.

= < <= > >= can take multiple operands, e.g.,(> x y z) means x > y > z.

number? tests whether a value is a number.

complex?, real?, rational?, integer? test what kind of number.

number->string

string->number (has ways to indicate parse errors)

5 / 33

Some String Operations(string-length str): length.

(string-ref str k): character at position k (start from 0).

(substring str i j) like Python’s str[i:j].(substring str i) like str[i:].

(string-append str1 str2 ...): concatenation.(string-append) gives the empty string.

string=? string<? string<=? string>? string>=?

Comparisons. Can take multiple operands. There are alsocase-insensitive versions.

string? tests whether a value is a string.

6 / 33

EqualityEquality is a mess. Three kinds, each with its intention and “but”s.

I eq? Good for booleans and symbols.

Pointer equality for most aggregates, e.g., strings, lists.

Complicated rules for numbers.

Intention: Fast, just compare two machine words.

I eqv? Good for characters.

Complicated rules for numbers, and different from eq?.(Rationale: Choices in treating floating-point’s NaN andsigned zero.)

Most other types: Same as eq?.

I equal? Structural equality for most aggregates, i.e.,comparing contents.

7 / 33

DefinitionsGive cool names to cool things.

Examples:

; A constant.(define my-width/height (/ 4 3))

; A function with 1 parameter.(define (greet person)(string-append "hello " person))

; A function with 2 parameters.(define (my-log base x)(/ (log x) (log base)))

Recursion is allowed.

8 / 33

Anonymous Functions/Procedures (lambda, λ)(Terminology: “procedure” expected to be effectful, e.g., I/O;“function” expected to be effect-free. Not strictly enforced though.)

Can write a function without name; “procedure expression”.(lambda (param1 param2 ...) body)

Example:(lambda (base x) (/ (log x) (log base)))

Example usage:( (lambda (base x) (/ (log x) (log base))) 128 2)

Function definition (define (f x y) body) is short-hand for(define f (lambda (x y) body)).

May also write λ.

9 / 33

ConditionalsIf-then-else: (if test then-expr else-expr)Actually test can be non-boolean—treated as true.

Multiple conditions:

(cond[(> x y) (sin x)][(< x y) (cos y)][else 0])

If x > y then sin x; else if x < y then cos y; else 0.

Test results can be non-boolean—treated as true. Furthermoreyou can obtain and use it:

(cond[(+ 4 2) => (lambda (x) (* x x))][else 0])

This gives 36.

10 / 33

and, or As Conditionals(and expr expr ...)

Evaluates from left to right, stops as soon as #f happens.Otherwise, the last expr is the answer.Examples:(and 42 #f "hello") gives #f(and 42 ’hey "hello") gives "hello"(and) gives #t

(or expr expr ...)

Evaluates from left to right, stops as soon as non-#f happens, andthat’s the answer. Otherwise, the answer is #f.Examples:(or 42 ’hey "hello") gives 42(or (and #f) #f) gives #f(or) gives #f

11 / 33

Local Bindings—Non-RecursiveLocal definitions for use in just one expression.

(let ([x expr1][y expr2])

(+ x y (* 2 x y)))

“Compute x + y + 2xy, where x = expr1 and y = expr2”.

expr1 and expr2 cannot see the local x or y; they see outer names.

(let ([x 3])(let ([x (* x x)]) ; (* 3 3)x))

gives 9 and is not a recursion.

let* allows later bindings to see earlier bindings.

(let* ([x 5][y (+ x 1)]) ; (+ 5 1)

(+ x y (* 2 x y)))

12 / 33

Local Bindings—Recursiveletrec allows recursive bindings (self or mutually).

(letrec ([fac(lambda (n)(if (= n 0) 1 (* n (fac (- n 1)))))]

[even(lambda (n)(or (= n 0) (not (odd (- n 1)))))]

[odd(lambda (n)(not (even (- n 1))))])

(even (fac 5)))

13 / 33

Recommended Code LayoutJokes: Pythonic Scheme inspired by Pythonic Java.

Serious:

I Open parentheses then immediately first word.I Procedure definition: Body starts on new line, indented.I Long expression: Parts start on new lines, indented.I Closing parentheses not on new lines.

Most editors have Scheme modes that can do these for you.

14 / 33

Compound Data: Pairs And Lists“Cons cell”, 2-tuple, pair. Syntax: (cons x y).Can imagine a pair of pointers.Short-hand if both fields are literals: ’(5 . "hello").

What if the 2nd field is (points to) a cons cell again, and its 2ndfield is a cons cell again,. . . ? Singly-linked list.

Special support for lists:

I Empty list: ’()I (list x y z) = (cons x (cons y (cons z ’())))I List literal: ’(42 "hi" Chrome)

The Chrome means the symbol ’Chrome.

15 / 33

Some Pair And List OperationsTests: pair?, list?, null?

First field of a pair: carSecond field of a pair: cdr

First item of a list: first, same answer as car, but only for lists.Tail of a list: rest, same answer as cdr, but only for lists.

length

(list-ref lst i): item at position i.

(append lst1 lst2 ...): concatenation.

reverse

Higher-order functions: map, filter, foldr, foldl . . . discussedlater.

16 / 33

Compound Data: User-Defined Records(struct dim (width height)) creates a new record type of twofields, with these support utilities:

I (dim 4 7) constructs a value of this type.I dim? tests for this type.I dim-width, dim-height: field accessors.

struct-copy: Clones a record while replacing some field values.Original record unchanged—”functional update”.

(define d1 (dim 4 7))(define d2 (struct-copy dim d1 [width 5]))

Then d2 is (dim 5 7).

17 / 33

Pattern MatchingTest for literal, cons cell, or record type, and get the content too.

(struct dim (width height))

(define (foo x)(match x[’() ’nada][(cons b _) b][(dim w h) (* w h)]))

(foo ’()) gives ’nada(foo ’(1 2 3)) gives 1(foo (dim 4 7)) gives 28

18 / 33

Input And Output: stdin, stdout, stderrstdout and stdin:

(display 5), (display "hello")(newline), (displayln 5), (displayln "hello")(printf "~a = ~a~n" "price" 5)

Aside: (format "~a = ~a~n" "price" 5) gives the string ratherthan outputs it.

(read-line)

(read-string 10) reads up to the upper bound.If EOF, returns eof, can use eq? or eof-object? to test.

stderr:eprintf is like printf but goes to stderr.

19 / 33

Input And Output: PortsRacket has ports, analogous to Java Reader/Writer—behind it canbe file, string, network connection, message queue, user-defined.

(call-with-output-file* "out.txt"#:mode ’text #:exists ’replace(lambda (op)(displayln 6 op)(fprintf op "~a~n" 7)))

(let ([s (call-with-input-file* "in.txt" #:mode ’text(lambda (ip)(read-string 10 ip)))])

(displayln s))

20 / 33

SequencingYou may want to evaluate multiple expressions (in the order youspecify) because the point is their effects.

(begin(displayln "Please enter your name")(read-line))

It returns what the last expression returns.

(begin0 expr1 expr2 ...)

returns what expr1 returns. (The other expressions are stillevaluated.)

(when (> x 0) expr1 expr2 ...)

If true, evaluates the expressions, returns what the last onereturns. If false, returns “#<void>”.

21 / 33

SequencingSome constructs already support multiple expressions andsequencing, you don’t need to wrap with begin:

I Conditionals and pattern matching:(cond [test expr1 expr2 ...] [...] ...)

(match expr [pattern expr1 expr2 ...] [...] ...)

I Procedure bodies:(define (f x) expr1 expr2 ...)

(lambda (x) expr1 expr2 ...)

I Bodies of let etc.:(let ([x 5] [y 42]) expr1 expr2 ...)

Also, and, or already do sequencing.

22 / 33

Mutable Variables(define v 5)(define (f x) (+ v x))(f 0) ; gives 5(set! v 6)(f 0) ; gives 6

Mutable pairs, lists, strings, arrays. . . are also available.

Use mutation judiciously. It is much less necessary than mostpeople think.

23 / 33

map

(map f (list x y z)) equals (list (f x) (f y) (f z))

Can you write your own version?

(define (my-map f lst)(match lst[’() ’()][(cons hd tl) (cons (f hd) (my-map f tl))]))

Remark: Racket’s map is more general—can take several lists, e.g.,

(map + ’(1 2 3) ’(10 20 30)) equals ’(11 22 33).

24 / 33

filter

(filter number? ’(9 "4" 0 "1" "6" 5)) equals ’(9 0 5).

Can you write your own version?

(define (my-filter pred lst)(match lst[’() ’()][(cons hd tl) (if (pred hd)

(cons hd (my-filter pred tl))(my-filter pred tl))]))

25 / 33

foldr MotivationSum up a list, write your own recursion, first way:

(define (my-sum lst)(match lst [’() 0]

[(cons hd tl) (+ hd (my-sum tl))]))

Multiply a list, write your own recursion, first way:

(define (my-product lst)(match lst [’() 1]

[(cons hd tl) (* hd (my-product tl))]))

What is different? What stays the same? Can you factor it out?

(define (foldr binop z lst)(match lst[’() z][(cons hd tl) (binop hd (foldr binop z tl))]))

26 / 33

foldr

If your function satisfies:

I (g ’()) equals zI (g (cons hd tl)) equals (binop hd (g tl))

Then (g lst) equals (foldr binop z lst)

Intuitively, they look like a ⊕ (b ⊕ (c ⊕ z)), writing ⊕ for binop, if thelist is (list a b c).

Not always obvious. Some refactoring can help. Telltale sign: Therecursion is simply on the list tail.

Example: Next slide shows why my-map is a foldr.

Example: my-filter is also a foldr.

27 / 33

my-map is a foldr(define (my-map f lst)(match lst[’() ’()][(cons hd tl) (cons (f hd) (my-map f tl))]))

=(define (my-map f lst)(define (g lst)(match lst[’() ’()][(cons hd tl) (cons (f hd) (g tl))]))

(g lst))

=(define (my-map f lst)(define (binop x r) (cons (f x) r))(define (g lst)(match lst[’() ’()][(cons hd tl) (binop hd (g tl))]))

(g lst))

=(define (my-map f lst)(define (binop x r) (cons (f x) r))(foldr binop ’() lst))

28 / 33

foldl MotivationSum up a list, write your own recursion, second way (accumulator):

(define (my-sum lst); (loop accum lst) computes accum + sum of lst(define (loop accum lst)(match lst [’() accum]

[(cons hd tl) (loop (+ accum hd) tl)]))(loop 0 lst))

Multiply a list, write your own recursion, first way:

(define (my-product lst); (loop accum lst) computes accum * product of lst(define (loop accum lst)(match lst [’() accum]

[(cons hd tl) (loop (* accum hd) tl)]))(loop 1 lst))

What is different? What stays the same? Can you factor it out?

29 / 33

foldl(define (foldl binop a lst)(match lst[’() a][(cons hd tl) (foldl binop (binop a hd) tl)]))

Intuitively, (foldl binop a (list x y z)) looks like((a ⊕ x) ⊕ y) ⊕ z, writing ⊕ for binop.

30 / 33

Procedure-Call Stack(define (f n) (... (f (- n 1)) ...)(displayln (+ (f 4) (f 1) (f 6)))

Control-flow jumps into f; later automagically knows where toreturn to.

“Sufficiently elegant solutions are indistiguishable from magic.”

A stack is used to remember where to return to. “Call stack”.

Benefit: Supports recursion (self and mutual).Price: Each invocation holds up Θ(1) space until it finishes.

(Invented by Peter Naur for Algol. Before him, each procedure wasgiven fixed space for return address. Recursion disallowed. Peopledidn’t believe Naur when he got this to work.)

You will understand this intimately in Part II of the course.

31 / 33

Non-Tail Calls and Tail CallsNon-tail call: There is post-processing after the call returns.

(define (my-sum lst)(match lst [’() 0]

[(cons hd tl) (+ hd (my-sum tl))]))

Takes Θ(n) space (if n is the list length).

Tail call: No post-processing after the call returns. No need toremember return address, just jump.

(define (my-sum lst)(define (loop accum lst)(match lst [’() accum]

[(cons hd tl) (loop (+ accum hd) tl)]))(loop 0 lst))

Literally a loop. Takes O(1) space.

32 / 33

Nested LambdasA procedure that returns a procedure.

(define map1 (lambda (f) (lambda (lst) (map f lst))))

Sample usage: ((map1 abs) ’(-1 -4))

Real usage: (map (map1 abs) ’((-1 -4) (-2 -5)))

Note: (map (map abs) ...) doesn’t work—wrong number ofparameters.

33 / 33