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 withncopies of the empty list embedded in it, e.g.:> (onion 0) '() > (onion 1) '(()) > (onion 2) '((())) > (onion 3) '(((()))) > (onion 10) '((((((((((()))))))))))
If
nis less than 0, then callerrorwith 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
lstis empty, then call theerrorfunction 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 onlstrepeat, 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. Assumelstis 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
expis a standard Racket function.(is-relation? lst)returns#tif, and only if,lstis a non-empty list of lists of the form(a b), where bothaandbare 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#tif, and only if,lstis 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#tifxis an atom, and#fotherwise. 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-atoms1using basic recursion (and no higher-order functions, such asmap,fold, orfilter).Re-do question 2 above, but this time call the function
count-atoms2and don’t use recursion. Instead, implement using only higher-order functions, such asmap,fold, orfilterWrite a function called
(check f suite)to help test ifcount-atoms1andcount-atoms2are correct. Here,fis an atom counting function (i.e.count-atoms1orcount-atoms2), andsuiteis 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
fpasses 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-atoms1is correctly implemented. Then:> (check count-atoms1 test-suite) "all tests passed"
Suppose
count-atoms-badis 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
checkis done, make sure to use it to actually testcount-atoms1andcount-atoms2.Hint: the
formatfunction is a good way to make formatted strings.