CS 341 - Survey of Programming Languages
Scheme for Java Programmers - Exercises |

Index |

**REPL: Read-Eval-Print Loop****Operators and Operands****Identifiers, Whitespace, and Comments****Fundamental Types****Variables and Assignments****Pairs and Lists****Lambda Expressions****Control Constructs****Binding Constructs****Equivalence****More List Operators****Output****Next Steps****Online Resources**

REPL: Read-Eval-Print Loop |

- Find an online survey of programming languages used for (1) general programming, or (2) an application area of interest to you. Which of the most popular programming languages are interpreted?
- What are the advantages/disadvantages of interpreted versus compiled languages?
- In the preface to Essentials of Programming Languages, what does Abelson refer to as "the most fundamental idea in computer programming"? Describe this idea in your own words.
- In the preface to Essentials of Programming Languages, what are Abelson's three reasons that a programmer should learn about interpreters?

Operators and Operands |

Given that sqrt is Scheme's square root operator, express the following Java expressions in Scheme and give the printed evaluated value:

`(1 + 2) * (4 - 3)``Math.sqrt(1/4 + 5/16)``(1 + 2 + 3 + 4 + 5)`(Hint: How many operands can + take?)`((3 - 2) / 4 + 5 * (6 - 7))`

Identifiers, Whitespace, and
Comments |

1. Looking through the index of R5RS, which of the non-alphanumeric characters tend to be used the most? How are they used, i.e. what implicit meaning to they have?

2. Are block comments possible in Scheme? If so, how?

3. Take a complex expression from the tutorial and rewrite it on a single
line. Why is it difficult to visually comprehend? That is, what
benefit is derived from breaking the expression across lines and using proper
indentation?

Fundamental Types |

1. How does one test type membership in Scheme?

2. How are the Boolean values true and false denoted in Scheme?

3. Express the following Java expression in Scheme: `(!false && (true ||
false))`

4. How do numeric types differ between Scheme and Java?

5. What is the value of the following Scheme expression?: `(+ (/ 1 2) (/ 1 3))`

6. Suppose you have a test string called `test-string` (e.g. `(define
test-string "testing"))`. Assuming `test-string` has at least 2 characters, what is
the expression that produces a string with all but the first and last
characters?

7. What function tests the equality of two strings? What's the easiest way to test case-insensitive equality of strings?

8. What functions convert numbers to strings and strings to numbers?

9. How is a newline character denoted in Scheme?

10. What is the difference between `count` and `'count` (i.e. `(quote count)`) in
Scheme? What is the simplistic meaning of the single-quote?

Variables and
Assignments |

1. Define a variable `count`, initialized to `0`. Then
increment the variable by `1`.

Pairs and Lists |

1. What is the difference between `(cons 'a 'b)` and `(list 'a 'b)`?
How would each appear in pair notation?

2. Given `(define l (list '(a b c) '(d e f) '(g h i)))`, how would you access
each of symbols `'c` through `'i` in `l`? For example, `'b` is accessed by `(car (cdr (car l)))`
or the abbreviated form `(cadar l)`.

3. As in 2, demonstrate access for each of the symbols of this complex
structure: `(define s '(((a b) (c . d)) ((e . f) . (g h))))`

4. What were the more intuitive names for car and cdr and why were they more sensible?

5. What is the pair notation expression `'((a . (b . ())) . (c . ()))`
equivalent in list notation?

6. Using only `cons`, the empty list, and symbols, show how to construct each
of the following: `'(c)`, `'(a b)`, `'((a b) (c))`. You may define variables to
store and reuse values.

Lambda Expressions |

1. One way to describe a line is to make *y* a function of *x*.
The common notation is *y *= m*x* + b, where m is the slope and b is
the *y*-intercept. Define an unnamed function that takes *x* as
input, and evaluates *y* given that m = 2 and b = 1.

2. Define `get-y` to be the unnamed function of (1). Use `map` to
apply it to the list `'(-2 -1 0 1 2)`.

3. Define a function `make-get-y` that takes `m` and `b` as input and returns
an unnamed function which takes *x* as input and evaluates m*x* + b.
Note: You're defining a function that *returns a function. *That is,
the body expression of the lambda expression is also lambda expression.

4. Define `get-y2` to be the same function as in (2), but create the function
using `make-get-y` of (3). Use `map` to apply `get-y2` to the list
'(-2 -1 0 1 2).

5. A degree-2 polynomial has the form *y* = a**x***x* + b**x*
+ c. Define an unnamed function that takes *x* as input, and
evaluates *y* given that a = 1, b = 2, and c = 3.

6. Define `polynomial` to be the unnamed function of (5). Use `map` to
apply it to the list `'(-2 -1 0 1 2)`.

7. Define a function `make-polynomial` that takes `a`, `b`, and `c` as input and
returns an unnamed function which takes *x* as input and evaluates `a`**x***x*
+ `b`**x* + `c`.

8. Define `polynomial2` to be the same function as in (6), but create the
function using `make-polynomial` of (7). Use map to apply `polynomial2`
to the list `'(-2 -1 0 1 2)`.

9. What is the difference between `eval` and `apply`? What operands do each
take and what is the result of the their application to these operands?

Control Constructs |

1. Assuming that the arguments after the if condition do not evaluate to
`#f`, define a function `my-if` that behaves like `if`, but is defined in
terms of `or` and `and`. Test `my-if` with the following expressions: `
(my-if #t 'first 'second) (my-if #f 'first 'second)`

2. Define a recursive function `last` that returns the last element of a
list operand. You may assume that the list has at least one element.
Show that `last` works for `'(1) '(1 2)` and `'(1 2 3 4 5)`. Hint: The `null?`
predicate is useful here.

3. Define a recursive function `factorial` that takes positive integer *
n* and evaluates to *n*!. Show the evaluation of 1!, 2!, and 5!.

4. Define a recursive function `sum-of-ints` that takes a positive
integer *n* and evaluates the sum 1 + 2 + ... + *n*. Show the
evaluation for *n *= 1, 5, and 10.

5. Define a recursive function `fib` that takes a non-negative integer *
n *and returns the *n*th Fibonacci number. `(fib 0)` is `0`, `(fib 1)`
is `1`, and for all other *n*, `(fib n)` is the sum of the two preceding
numbers in the Fibonacci sequence. Hint: Use cond for these three cases.
Use map to test fib on each of the integers in the list

6. Devise a recursive Fibonacci function `fib-tr` that doesn't generate a tree of
recursive calls. Rather make it tail recursive, computing from the
beginning of the sequence forward until it reaches the desired number. You
will need to define an auxiliary function to pass forward the previous two
numbers, the current sequence number, and the desired sequence number. Use
map to test `fib-tr` on each of the integers in the list `'(0 1 2 3 4 5 6 7 8)`.

For example, if you called your auxiliary function fib-tr-aux with the parameters in the order given above, the call sequence for (fib-tr 6) might be:

(fib-tr 6) => (fib-tr-aux 0 1 1 6) => (fib-tr-aux 1 1 2 6) => (fib-tr-aux 1 2 3 6) => (fib-tr-aux 2 3 4 6) => (fib-tr-aux 3 5 5 6) => (fib-tr-aux 5 8 6 6) => 8

7. Define the logical operator `nand` (not and) using `not` and `and`.
Does short-circuiting evaluation occur in your implementation? Why or why
not? Hint: You can test whether or not the second operand is evaluated by
making it a `display` statement.

8. Define the logical operator `nor` (not or) using `not` and `and`.
Does short-circuiting evaluation occur in your implementation? Why or why
not? Hint: You can test whether or not the second operand is evaluated by
making it a `display` statement.

9. Define a function `direction` that takes a number of degrees degrees
and returns a symbol as follows: -45 < degrees <= 45 returns `'right`, 45 <
degrees <= 135 returns `'up`, 135 < degrees <= 225 returns `'left`, 225 < degrees
<= 315 returns `'down`, and all other values have 360 added or subtracted until
they map to a number (and thus symbol) within these ranges. Use `map` to
test `direction` on each of the integers in the list `'(-720 0 90 180 270 720)`.

10. Define a function `digit-case` that takes an non-negative integer and
uses a `case` statement to return either `'even-digit`, `'odd-digit`, or `'not-a-single-digit` accordingly.
Use `map` to test `digit-case` on the list `'(0 1 2 3
4 5 6 7 8 9 10)`.

11. Define function `last-do` that implements last of (2) using a `do`
loop. Show that it works for the same test cases.

12. Do (3) using an alternative implementation `fact-do` that uses a `do`
loop. Show that it works for the same test cases.

13. Do (4) using an alternative implementation `sum-do` that uses a `do`
loop. Show that it works for the same test cases.

14. Do (5) using an alternative implementation `fib-do` that uses a `do`
loop. Hint: This is much like the approach for (6). Show that it
works for the same test cases.

15. Compare and contrast `begin` and `and`. What is the essential
difference?

Binding Constructs |

So far, we've introduced global variables with "define", and local variables with the lambda argument list. We haven't yet shown how to introduce local variables that aren't procedural arguments. There are three forms for doing so: "let", "let*", "letrec".

(let ((<var1> <binding-expression1>) (<var2> <binding-expression2>) ... (<varN> <binding-expressionN>)) <local-expression>)The binding expressions are all evaluated together in the surrounding outer variable context. Then they are assigned in some unspecified order to the local variables. The local expression is then evaluated with respect to the new local variable environment. It's helpful to think of the new local environment being built on top of the outer variable environment. If one of the local variable has the same name as a variable in the surrounding environment, it

Here are some examples:

> (let ((x 2) (y 3)) (+ x y)) 5 > (let ((x 2)) (let ((x 3) (y (+ x 4))) (+ x y))) 9

Look at the latter example more closely. Why isn't the answer 10? If we wish to have the local variables be created and bound in sequence, we use the "let*" operator. "let*" shares the same syntax, but has the meaning of performing a bunch of nested single-binding "let"s.

> (let ((x 2)) (let* ((x 3) (y (+ x 4))) (+ x y))) 10 > (let ((x 2)) (let ((x 3)) (let ((y (+ x 4))) (+ x y)))) 10

"let" is more natural if you're only referring to the outer variable context. "let*" is more natural if there are non-circular dependencies between variables such that they can be bound in sequence. But what if the variables are defined in terms of one another? In Scheme, you can recursively define variables using "letrec". Here's an example (based on a R5RS example) in which two variables holding procedures are defined according to one another:

(letrec ((even? (lambda (n) (or (zero? n) (odd? (- n 1))))) (odd? (lambda (n) (and (not (zero? n)) (even? (- n 1)))))) (even? 42)) #t

In general, prefer the simplest binding construct according to your needs.

Equivalence |

There are three general *equivalence predicates* in Scheme: "eq?", "eqv?",
and "equal?". "eq?" is most discriminating and strict in its
interpretation of equality. "equal?" is the least discriminating and
strict in its interpretation of equality. Detailed description of these
can be found in R5RS, but in a nutshell:

- eqv? - returns #t if two pieces of data should normally be considered the same; you'll generally use this.
- eq? - may makes some finer distinctions than eqv? for implementation specific situations, e.g. distinguishing between empty lists and empty vectors
- equal? - recursively compares data to see if they would print the same.

For example:

> (eqv? 'a 'a) #t > (eqv? '() '()) #t > (eqv? '(a) '(a)) #f > (equal? '(a) '(a)) #t > (equal? '(- a (+ b 1)) '(- a (+ b 1))) #t

You'll generally use "=" for comparing exact numbers, "eqv?" for comparing simple objects, and "equal?" for comparing complex objects.

More List Operators |

Since list-processing is the forte' of Scheme and Lisp, there are many useful list operators. Here are some basic examples:

> (length '(a b c)) 3 > (append '(a b c) '(d e f)) (a b c d e f) > (reverse '(a b c)) (c b a) > (list-tail '(a b c d e f) 3) (d e f) > (list-ref '(a b c d e f) 3) d > (memv 'c '(a b c d e f)) (c d e f) > (memv 'z '(a b c d e f)) #f > (define assoc-list '((a 1) (b 2) (c 3))) > (assv 'b assoc-list) (b 2) > (assv 'z assoc-list) #f

"list-ref" and "list-tail" can be used to access list elements or tail sublists by index. "memv" can be used to check list membership. "assv" takes a key and an association list (a.k.a. a-list) of key-value pairs and either returns the relevant pair or #f if the key is not present.

Output |

As we will be making use of some non-standard input procedures in our programming, we will only cover simple output here.

> (write assoc-list) ((a 1) (b 2) (c 3)) > (write "string") "string" > (display "string") string > (write-char #\newline) > (newline) >

"write" will display any Scheme data in its standard representation form. Thus, a string which is written will be enclosed in double-quotes. The "display" operator will display string in their normal display representation as if each string character were written individually using "write-char". The "newline" operator simply writes a new line to the current output port. As noted earlier, a port is a basic data type. More details on input/output can be found in R5RS section 6.6.

Next Steps |

We haven't covered Scheme's library exhaustively (e.g. vectors). Nor
have we covered it's semantics or implementation restrictions in rigorous
detail. What we have accomplished is a blitz introduction that should give
you a good starting foundation for *practice*. Learning is
experiential. Echoing Socrates, Charlie Peacock sang, "We can only possess
what we experience." Reading about Scheme is nothing like programming
Scheme.

Your real education begins as you approach problems, simple or complex, and
translate your solutions into Scheme code. At first, there will be a
disconnect between how you think and the how you program Scheme. As all
programming languages, Scheme cannot change what it is. However, your mind *can* change and will learn to translate thoughts into a very different
paradigm than it is used to. This stretching exercise of the mind will
give you a broader view of programming, a great comfort with recursion, and, if
you're open to it, a yearning for functional programming.

Online Resources |

- Standard Scheme Language Reference (R5RS) (PostScript, HTML)
- Dr. Scheme home page
- Essentials of Programming Languages (2nd ed.) home page
- How To Design Programs text

© 2003 Todd Neller