CSE431 Sample Final handout #7
For Scheme questions, you are not allowed to use mutating functions, you are
not allowed to use vectors, and in terms of functions that take lists as an
argument, you are limited to the following:
basic: length, car, cdr, cons, append, list, member, remove, assoc, reverse
positional: cadr, caddr, cadddr, cddr, cdddr, first, second, third
higher-order: map, filter, foldl, foldr, sort, andmap, ormap
predicates: null?, pair?, list?
Some problems may have additional constraints. For Ruby problems, unless
otherwise noted, you can use all of standard Ruby, but you can't use operations
that require loading specialized libraries.
For both Scheme and Ruby questions, you may define helper functions/methods,
although the helper functions in Scheme must be localized to the function so
that they do not become part of the top-level environment. For the Scheme
questions, you may call any Scheme function that is included as a problem on
this exam, whether or not you correctly solve that problem. For the Ruby
questions, you may call any Ruby method that is included as a problem on this
exam, whether or not you correctly solve that problem.
1. (10 points) For each sequence of Scheme expressions below, indicate what
value the final expression evaluates to. If an expression produces an error
when evaluated, write "error" as your answer.
(2 points) Part A:
(define a 101)
(define b 103)
(define c 105)
(let ((c (+ a c))
(a (+ b c)))
(list a b c))
(2 points) Part B:
(define a 101)
(define b 103)
(define c 105)
(let* ((b (+ a b))
(a (+ b c)))
(list a b c))
(2 points) Part C:
(define x 101)
(define (f n) (+ x y n))
(define y 103)
(define a (f 2))
(define x 105)
(define b (f 2))
(define c a)
(set! a 19)
(list a b c)
(4 points) Part D:
(define a '(1 2 3))
(define b '(2 3))
(define c (append (cdr b) a))
(define d (list a b))
(list c (eq? a (car d)) (eq? (cdr a) b) (eq? (cdr c) a))
2. (5 points) Define a Scheme function called occurrences that takes a value
and a list as arguments and that returns the number of occurrences of the
value in the list. Your function should use a deep equality comparison,
although it should not search for embedded occurrences (those inside a
sublist). You may assume that the second argument is a list. For example:
> (occurrences 3 '(7 9 2 4 a (3 2) 3 "hello" 3))
2
> (occurrences 'a '(3 a b a 19 (a b) c a))
3
> (occurrences '(a b) '(a b c (a b) d 3 a b (a b) 7))
2
> (occurrences 2 '(1 (2 3) 4 5 (2 2 2)))
0
3. (5 points) Define a Scheme function called remove-all that takes a value and
a list as arguments and that returns the result of removing all occurrences
of the value from the list. Your function should use a deep equality
comparison, although it should not remove embedded occurrences (those inside
a sublist). For example:
> (remove-all 2 '(1 2 3 4 2 7 2 (1 2 2) (3 (2 4) 2)))
'(1 3 4 7 (1 2 2) (3 (2 4) 2))
> (remove-all 'a '(a b c (a b) a (c) a a))
'(b c (a b) (c))
> (remove-all '(a 1) '(13 (a 1) 7 a 1 (a 1) 4 a 1))
'(13 7 a 1 4 a 1)
> (remove-all 2 '(1 3 3 4 (2 2 2) 7 (2 1 2)))
'(1 3 3 4 (2 2 2) 7 (2 1 2))
Your function must run in O(n) time where n is the length of the list. You
are not allowed to use the standard function called remove.
4. (10 points) Define a Scheme function called maximum that takes a list of
numbers as an argument and that returns the largest value in the list.
For example:
> (maximum '(1 13 -408 273 17 304 8 9))
304
> (maximum '(7.4 28 13.9 3.14159 2.71828 42))
42
You may assume that the value you are passed is a list and that it contains
only numbers. Your function should call the error procedure with the
message "max of empty list" if passed an empty list:
> (maximum '())
. . max of empty list
Your function must be efficient in several respects. It should be
tail-recursive, it should run in O(n) time where n is the length of the
list, and it should check the error condition only once. You are not
allowed to call the standard function called max in solving this problem.
5. (10 points) Define a Scheme function called pattern that returns a new value
that represents the kind of data passed to it. Numbers, strings, symbols,
and booleans should be converted into the symbols number, string, symbol,
and boolean, respectively. For example:
> (pattern 13)
'number
> (pattern 13.4)
'number
> (pattern "hello")
'string
> (pattern 'a)
'symbol
> (pattern #t)
'boolean
A list of values should be converted into the list of patterns for each
value in the list, as in:
> (pattern '(13 13.4 "hello" a #t))
'(number number string symbol boolean)
This conversion should occur at all levels of a list, preserving the list
structure:
> (pattern '(13 (13.4 "hello" (a #t)) (14 (a b))))
'(number (number string (symbol boolean)) (number (symbol symbol)))
> (pattern '((((17)))))
'((((number))))
Values that are not of one of these types (e.g., dotted pairs) should be
included without modification in the result:
> (pattern '(3 (3 . 4) a "hello" (a . b)))
'(number (3 . 4) symbol string (a . b))
Remember that Scheme has a series of predicates for testing whether data is
of a certain type (number?, string?, symbol?, boolean?, list?).
6. (10 points) Define a Scheme function called switch-pairs that takes a list
as an argument and that returns the list obtained by switching successive
pairs of values in the list. For example:
> (switch-pairs '())
'()
> (switch-pairs '(a b))
'(b a)
> (switch-pairs '(a b c d e f))
'(b a d c f e)
> (switch-pairs '(3 a (4 5) (6 7) 9 (1 2 3)))
'(a 3 (6 7) (4 5) (1 2 3) 9)
If the list has an odd number of values, then the final element should not
be moved. For example:
> (switch-pairs '(a))
'(a)
> (switch-pairs '(a b 3 (4 5) 9))
'(b a (4 5) 3 9)
Your function should call the error procedure with the message "not a list"
if passed something other than a list:
> (switch-pairs 3)
. . not a list
Your function must run in O(n) time where n is the length of the list.
7. (10 points) Define a Scheme function called overlap that takes two sorted
lists of numbers as arguments and that returns a list of the values in
common between the two lists. For example:
> (overlap '(1 2 2 3 3 4 7 7 7) '(1 2 2 2 2 3 5 7 8))
'(1 2 2 3 7)
You may assume that your function is passed two lists, that they contain
only numbers, and that the numbers appear in sorted (nondecreasing) order.
But as in the example above, they might contain duplicates. Any given
number from a list can match at most once. Thus, the two occurrences of 2
in the first list can match 2 of the occurrences of 2 in the second list,
but the additional occurrences of 2 in the second list do not appear in the
result because they aren't matched. Similarly, the result includes only one
7 even though the first list has three occurrences of 7 because the second
list has only 1 such occurrence.
Your function must take advantage of the fact that the lists are sorted. In
particular, your function has to run in O(n + m) time where n is the length
of the first list and m is the length of the second list.
8. (10 points) Define a Scheme function called histogram that takes a list of
values as an argument and that returns a list of 2-element lists where each
2-element list contains a value from the original list followed by a count
of that value in the original list. Each value from the original list
should produce exactly once such pair and they should appear in the order in
which the values first appear in the original list. For example:
> (histogram '(1 3 2 5 1 3 3 3))
'((1 2) (3 4) (2 1) (5 1))
The result indicates that the value 1 appeared twice in the original list,
the value 3 appeared 4 times, the value 2 appeared 1 time, and the value 5
appeared 1 time.
You may assume that your function is passed a list, but the list will not
necessarily contain simple values like numbers:
> (histogram '(a b c (a b) c (a b) b c a (a b)))
'((a 2) (b 2) (c 3) ((a b) 3))
9. (4 points) Assuming that the following variables have been defined:
x = 10..14
y = [2, 3, 5, 7, 11]
(1 point) Part A: What output is produced by the following code?
x.each {|n| puts n + 2}
(1 point) Part B: What output is produced by the following code?
for n in y do
puts n * 3
end
(2 points) Part C: What value is returned by the following expression?
x.map {|n| n % 2 == 0}
10. (6 points) Define a Ruby method called switch_pairs that takes an array as
an argument and that returns a new array that contains the same values with
successive pairs of values switched in order. For example:
>> switch_pairs([])
=> []
>> switch_pairs(["a", "b"])
=> ["b", "a"]
>> switch_pairs([1, 2, 3, 4, "a", "b", [3, 4], [5, 6]])
=> [2, 1, 4, 3, "b", "a", [5, 6], [3, 4]]
If the array has an odd number of values, then the final element should not
be moved. For example:
>> switch_pairs(["a"])
=> ["a"]
>> switch_pairs(["a", "b", 3, [4, 5], 9])
=> ["b", "a", [4, 5], 3, 9]
You may assume that your method is passed an array as a parameter. Your
method should not change the array that is passed as a parameter.
11. (6 points). Extend the Ruby Range class to have a new method called first
that takes an integer parameter n and that generates the first n values of
the range (i.e., that expects a block of code to be executed for the first
n values of the range). For example:
>> x = 1..10
=> 1..10
>> x.first(3) {|n| puts 2 * n + 1}
3
5
7
The method should use a default value of 1 for the parameter:
>> y = 10..20
=> 10..20
>> y.first {|n| puts n}
=> 10
You may assume that any value passed to the method is an integer. If the
value passed is less than 1, the method should not generate any values. If
the value passed is greater than the number of values in the range, you
should generate just the numbers in the range. You are not allowed to
construct an array in solving this problem. Don't worry about the fact
that this method overrides the predefined method called first.
12. (6 points). Extend the Ruby Array class to have a new method called
pairwise that uses a block to specify a 2-argument predicate, returning
true if the predicate is true of each adjacent pair of values in the array
and returning false otherwise. For example, we can use this method to test
whether an array is sorted as follows:
>> [3, 18, 24, 24, 42, 50, 75].pairwise {|x, y| x <= y}
=> true
The method compares the first adjacent pair (3, 18) to make sure that the
predicate is true, then compares the second adjacent pair (18, 24) to make
sure that the predicate is true, then compares the third adjacent pair (24,
24), and so on, until it compares the final adjacent pair (50, 75).
Below is an example of a predicate that tests whether a list is composed of
consecutive values.
>> [3, 18, 24, 24, 42, 50, 75].pairwise {|x, y| x + 1 == y}
=> false
>> [3, 4, 5, 6, 7, 8].pairwise {|x, y| x + 1 == y}
=> true
The method should return false if any pair fails the test, as in:
>> [3, 4, 5, 6, 7, 8, 10].pairwise {|x, y| x + 1 == y}
=> false
In this case, the only adjacent pair that fails is (8, 10). The method
should return true if there are no adjacent pairs to compare.
Your method should run in O(n) time where n is the length of the array and
should stop checking as soon as it finds a pair that fails. Your method
should not change the original array.
13. (8 points). Extend the Ruby Array class to have a new method called
mode that returns the most commonly occurring element in a sequence of
values (statisticians refer to this value as the mode). Assume that values
are arranged so that duplicates appear next to each other in the array.
Your method should return an array of length 2 that contains the mode
followed by the number of occurrences of the mode. For example:
>> [1, 8, 8, 8, 10, 14, 14, 25, 25].mode
=> [8, 3]
This result indicates that 8 occurred most frequently and that it occurred
3 times. If there is a tie, your method should return the first value:
>> [1, 8, 8, 8, 10, 14, 14, 14, 25, 25, 25].mode
=> [8, 3]
Three different values occurred 3 times (8, 14, and 25), but the method
returned the first one. The values might not be simple numbers:
>> [1, 1, 1, "foo", "bar", "bar", "bar", "bar", [7, 8]].mode
=> ["bar", 4]
>> [[1, 2], [1, 2], [1, 2], 7, 7, "foo", "foo"].mode
=> [[1, 2], 3]
The method should return nil and 0 occurrences if called on an empty array:
>> [].mode
=> [nil, 0]
Your method must run in O(n) time where n is the length of the array. As
noted above you should assume that duplicate values are grouped right next
to each other in the array. Your method should not change the array.