Assignment 1: Learning Racket¶
In all of the following questions, do not use any functions that end with
!
(i.e. no modifying functions), and don’t use any loops (just
recursion, or higher-order functions like map
, filter
, or fold
).
Please put all your code for this assignment into a single Racket file named
a1.rkt
, and submit it online in Canvas when you’re ready.
Warm Up¶
Implement each of the following functions. You can assume that the input for each function is always the correct type, so you don’t need to do any extra error-checking.
The my-
functions are similar to functions that are already in Racket —
don’t use those functions in your implementation!
(my-length lst)
returns the number of elements in a list, e.g.:> (my-length '()) 0 > (my-length '(a 1 (2 7))) 3
(onion n)
returns a list withn
copies of the empty list embedded in it, e.g.:> (onion 0) '() > (onion 1) '(()) > (onion 2) '((())) > (onion 3) '(((()))) > (onion 10) '((((((((((()))))))))))
If
n
is less than 0, then callerror
with a helpful message, e.g.:> (onion -2) . . onion: n negative
(my-last lst)
returns the last element of a list, e.g.:> (my-last '(a b c d)) 'd
If
lst
is empty, then call theerror
function with a helpful error message, e.g.:> (my-last '()) . . my-last: empty list
(double-up lst)
returns a new list that contains all the elements onlst
repeat, e.g.:> (double-up '(a 2 (c d))) '(a a 2 2 (c d) (c d))
(swap-ends lst)
returns a new list that is the same aslst
, except the first element and last element have been swapped. All the other elements are the same. For example:> (swap-ends '(a b)) '(b a) > (swap-ends '(a b c)) '(c b a) > (swap-ends '(a b c d)) '(d b c a)
(softmax lst)
returns a new list that is the standard (unit) softmax oflst
. Assumelst
is a non-empty list of numbers (which can be positive, negative, or 0). The numbers in the list returned by(softmax lst)
are all in the range [0,1], and sum to 1.For example:
> (softmax '(-1 2 3)) '(0.013212886953789414 0.26538792877224193 0.7213991842739685) > (softmax '(8 7 -2)) '(0.7310343155951328 0.26893249549828524 3.318890658198522e-005)
Note that
exp
is a standard Racket function.(is-relation? lst)
returns#t
if, and only if,lst
is a non-empty list of lists of the form(a b)
, where botha
andb
are integers. None of the pairs should be repeated.For example:
> (is-relation? '((2 4))) #t > (is-relation? '((6 2) (8 1) (2 2) (1 8))) #t > (is-relation? '((2 4) (1 5) (2 4))) #f > (is-relation? '((6.1 1) (1 5))) #f > (is-relation? '((6 1) (1 2 5))) #f > (is-relation? '()) #f > (is-relation? "a, b") #f
(is-function? lst)
returns#t
if, and only if,lst
is a relation, and all of its first elements are unique and all of its second elements are unique.For example:
> (is-function? '((2 4) (3 1) (1 8) (6 2))) #t > (is-function? '((2 4) (1 5) (2 5))) #f > (is-function? '((2 4) (3 1) (2 8))) #f > (is-function? 'cat) #f
Atom Counting¶
An atom is either a number or a symbol. Write the following Racket functions:
(atom? x)
returns#t
ifx
is an atom, and#f
otherwise. For example:> (atom? 3) #t > (atom? 'cat) #t > (atom? "house") #f > (atom? '(a b)) #f
(count-atoms1 lst)
returns the number of atoms in thelst
, not counting atoms that are in sub-lists. For example:> (count-atoms1 '()) 0 > (count-atoms1 '(a 2 (c d) "a" 4)) 3 > (count-atoms1 '(a 2 b c 3)) 5 > (count-atoms1 '((a) 2 "b" c 3))
Implement
count-atoms1
using basic recursion (and no higher-order functions, such asmap
,fold
, orfilter
).Re-do question 2 above, but this time call the function
count-atoms2
and don’t use recursion. Instead, implement using only higher-order functions, such asmap
,fold
, orfilter
Write a function called
(check f suite)
to help test ifcount-atoms1
andcount-atoms2
are correct. Here,f
is an atom counting function (i.e.count-atoms1
orcount-atoms2
), andsuite
is a list of (input expected-output) tests cases, e.g.:(define test-suite '((() 0) ((a) 1) ((2) 1) (("a") 0) ((a b) 2) ((3 1) 2) ((a 2) 2) (("a" b) 1) ((1 2 3 4) 4) ((a b c d) 4) ((1 a 2 3 b c) 6) ((1 a () 2 3 "m" b c (4 s 2)) 6)))
If
f
passes all the tests insuite
, then(check f suite)
returns the string"all tests passed"
. Otherwise, if any tests fail, it returns a list of strings, where each string is a nicely formatted message that describes which test case failed.For example, suppose
count-atoms1
is correctly implemented. Then:> (check count-atoms1 test-suite) "all tests passed"
Suppose
count-atoms-bad
is an incorrectly implemented atom counting function. It’s output should look something like this:> (check count-atoms-bad test-suite) '("fail: (f '(a)) returned 0; expected 1" "fail: (f '(2)) returned 0; expected 1" "fail: (f '(a)) returned 1; expected 0" "fail: (f '(a b)) returned 1; expected 2" "fail: (f '(3 1)) returned 1; expected 2" "fail: (f '(a 2)) returned 1; expected 2" "fail: (f '(a b)) returned 2; expected 1" "fail: (f '(1 2 3 4)) returned 3; expected 4" "fail: (f '(a b c d)) returned 3; expected 4" "fail: (f '(1 a 2 3 b c)) returned 5; expected 6" "fail: (f '(1 a () 2 3 m b c (4 s 2))) returned 5; expected 6")
Of course, the actual strings that get printed will depend upon the particular errors in
count-atoms-bad
, so these results aren’t unique.Note that all failed test cases are included, and that the strings are formatted in a way that is easy for a human to understand.
When
check
is done, make sure to use it to actually testcount-atoms1
andcount-atoms2
.Hint: the
format
function is a good way to make formatted strings.