Higher-Order Programming » Lab Sessions
Session 1
Assistant: Bjarno Oeyen (bjarno.oeyen@vub.be)
Identifiers and Bindings
In Scheme, symbols are used to represent variables. This means that the symbol string-length
is not the string-length
procedure itself, but it is the name how it can be found in the initial Scheme environment. The same is true for +
. In contrast with many other programming languages (like Java, or Python), +
resolves to a +
procedure (and unlike those languages, it's not part of the syntax of the language).
Exercise 1: Predict, on paper, the outcome of evaluating the following program. Keep track of the values assigned to the created bindings. Check your answer by entering each expression one-by-one in the REPL.
Exercise 2: Predict the output of the following program. Check your answer by evaluating it in the REPL.
Note in Scheme you have no return
statements. The last value produced by evaluating the body of the procedure, is the value returned when the procedure is applied.
Conditionals using if
and cond
Scheme has two forms that can be used for conditional computations: if
and cond
. Abstract examples using these two forms are shown below...
The classic if
in Scheme consists of three sub-expressions: a condition expression, a consequent expression, and an alternative expression. A Scheme evaluator will, upon evaluating an if
, first evaluate its condition expression. Depending on its evaluation result it will either evaluate the consequent expression (if the value produced by the condition expression is considered to be true), or the alternative expression (if the value produced by the condition expression is considered to be false).
Note that in Scheme, only false (#f
) is considered to be false. Every other value (numbers, strings, and of course the boolean #t
) are considered to be true.
In the case where only two sub-expressions are given (the condition expression, and the consequent expression), the evaluator will simply an unspecified value when the result of evaluating the condition expression is #f
.
In situations where there are more than two branches, its better to make use of cond
instead of nesting multiple if
expressions. When the evaluator encounters a cond
expression, it iterates over the different clauses, evaluating their test expression until one evaluates to a value considered to be true. It will then evaluate the sequence of expressions of that clause. The last clause can be an else-clause which is always evaluated if none of the other clauses were evaluated. If the else-clause is missing, the evaluator returns an unspecified value.
Exercise 3: The Collatz Conjecture is an unsolved conjecture in mathematics concerning a property of a sequence of numbers. Given the previous number in the sequence, the next number of the sequence can be found by applying the following function:
$$f(n) = \begin{cases} n / 2 \ &\text{if } n \ \text{is even} \\ 3 \times n + 1 & \text{if } n \ \text{is odd}\end{cases}$$
The conjecture states that when the sequence starts with a positive integer $n$, the sequence will, eventually, always reach the number 1.
Implement a procedure that, when given a number, calculates the next number in the sequence
Exercise 4: Almost all websites have a number of requirements for passwords if you wish to register an account. In this exercise, implement a procedure that checks the length of a password. When the password is too short (smaller than 8 characters) it prints “Password too short!
”. When the password is too long (larger than 20 characters) it prints “Password too long!
”. If the password is between 8 and 20 characters long nothing is printed.
In order to understand recursion, you first have to understand recursion
Exercise 5: Implement fac
which calculates the factorial of a given number.
$$n! = n * (n - 1)! \\ 0! = 1$$
Make use of tail-recursion. Is your solution a recursive or iterative process? Explain your reasoning, and implement it differently. Hint: What is tail-call optimisation?
Exercise 6: Implement a recursive procedure that given a number $n$ prints its Collatz sequence by making use of your solution of exercise 3.
Higher-Order Procedures
Exercise 7: Define a procedure twice
that takes a procedure of one argument as argument and returns a procedure that applies the original procedure twice. For example, if add1
is a procedure that increments a number by one, then (twice add1)
should return a procedure that increments a number by two. What value is returned when the following program is executed? You can assume that the given procedure takes only one argument.
Exercise 8: Define a procedure called compose
that composes two procedures such that the first procedure is applied on the result of applying the second procedure of one of the arguments of the procedure (i.e., $f \circ g$). You can assume that both procedure take only one argument.
Bonus Exercises
The solutions of these exercises will not be published. If you want to verify your solution, send it to the teaching assistant.
Exercise B1: Give the definition of variable f
for each of the following cases.
Expression | Output |
---|---|
f |
1 |
(f) |
2 |
(f 3) |
3 |
((f)) |
4 |
(((f)) 3) |
5 |
Make use of lambda
whenever possible. Note that there can be different solutions for the same case.
Exercise B2: Modify your solution of exercise 8 such that it can also handle the case where the first procedure has more than one parameter. Check section 4.1.4 of the R5RS specification regarding the syntactical forms of <Formals>
, make use of (apply proc lst)
to apply a procedure proc
on a variable-sized list of arguments lst
. Even if we have not yet used lists earlier, you should still be able to perform this exercise.
Exercise B3: Which special forms did you use in the exercises of this lab session?