I am confused about where I got wrong in translating this diagram into Racket code using "list" notation.
For the code, I wrote
(list 'greetings
(list 'howdy 'hi "hello")
(list "yo" 0.7734
(list 'hola 'bonjour)))
But it turns out that this code is incorrect. I looked up the list notation definition but I couldn't find what's wrong. Could anyone give me a hint about where I got wrong? Greatly appreciated!
The "yo", 0.7734, and (list 'hola 'bonjour) belong to the top level list:
(define sublist1 (list 'howdy 'hi "hello"))
(define sublist2 (list 'hola 'bonjour))
(list 'greetings sublist1 "yo" 0.7734 sublist2)
(list 'greetings (list 'howdy 'hi "hello") "yo" 0.7734 (list 'hola 'bonjour))
Related
I am trying to concatenate all elements in the list argument into a single list.
I have this code:
(define (concatenate . lsts)
(let rec ([l lsts]
[acc '()])
(if (empty? l)
acc
(rec (cons (list* l)
acc)))))
An example of output is here:
> (concatenate '(1 2 3) '(hi bye) '(4 5 6))
'(1 2 3 hi bye 4 5 6)
But I keep getting this error:
rec: arity mismatch;
the expected number of arguments does not match the given number
expected: 2
given: 1
Can someone please explain this?
Another answer explains the OP error,
and shows how the code can be fixed using append.
But there could be reasons for append to be disallowed in this assignment
(of course, it could be replaced with, for example, an inner "named let" iteration).
This answer will present an alternative approach and describe how it can be derived.
#lang racket
(require test-engine/racket-tests)
(define (conc . lols) ;; ("List of Lists" -> List)
;; produce (in order) the elements of the list elements of lols as one list
;; example: (conc '(1 2 3) '(hi bye) '(4 5 6)) => '(1 2 3 hi bye 4 5 6)
(cond
[(andmap null? lols) empty ] ;(1) => empty result
[else
(cons (if (null? (car lols)) ;(2) => head of result
(car (apply conc (cdr lols)))
(caar lols))
(apply conc ;(3) => tail of result
(cond
[(null? (car lols))
(list (cdr (apply conc (cdr lols)))) ]
[(null? (cdar lols))
(cdr lols) ]
[else
(cons (cdar lols) (cdr lols)) ]))) ]))
(check-expect (conc '() ) '())
(check-expect (conc '() '() ) '())
(check-expect (conc '(1) ) '(1))
(check-expect (conc '() '(1) ) '(1))
(check-expect (conc '() '(1 2) ) '(1 2))
(check-expect (conc '(1) '() ) '(1))
(check-expect (conc '(1) '(2) ) '(1 2))
(check-expect (conc '(1 2) '(3 4) ) '(1 2 3 4))
(check-expect (conc '(1 2 3) '(hi bye) '(4 5 6)) '(1 2 3 hi bye 4 5 6))
(test)
Welcome to DrRacket, version 8.6 [cs].
Language: racket, with debugging; memory limit: 128 MB.
All 8 tests passed!
>
How was this code derived?
"The observation that program structure follows data structure is a key lesson in
introductory programming" [1]
A systematic program design method can be used to derive function code from the structure
of arguments. For a List argument, a simple template (natural recursion) is often appropriate:
(define (fn lox) ;; (Listof X) -> Y ; *template*
;; produce a Y from lox using natural recursion ;
(cond ;
[(empty? lox) ... ] #|base case|# ;; Y ;
[else (... #|something|# ;; X Y -> Y ;
(first lox) (fn (rest lox))) ])) ;
(Here the ...s are placeholders to be replaced by code to create a particular list-argumented
function; eg with 0 and + the result is (sum list-of-numbers), with empty and cons it's
list-copy; many list functions follow this pattern. Racket's "Student Languages" support
placeholders.)
Gibbons [1] points out that corecursion, a design recipe based on result structure, can also
be helpful, and says:
For a structurally corecursive program towards lists, there are three questions to ask:
When is the output empty?
If the output isn’t empty, what is its head?
And from what data is its tail recursively constructed?
So for simple corecursion producing a List result, a template could be:
(define (fn x) ;; X -> ListOfY
;; produce list of y from x using natural corecursion
(cond
[... empty] ;(1) ... => empty
[else (cons ... ;(2) ... => head
(fn ...)) ])) ;(3) ... => tail data
Examples are useful to work out what should replace the placeholders:
the design recipe for structural recursion calls for examples that cover all possible input variants,
examples for co-programs should cover all possible output variants.
The check-expect examples above can be worked through to derive (1), (2), and (3).
[1] Gibbons 2021 How to design co-programs
Assuming you are allowed to call append, for simplicity. You have
(define (concatenate . lsts)
(let rec ([l lsts]
[acc '()])
(if (empty? l)
acc
(rec (cons (list* l) ; only ONE
acc) ; argument
))))
calling rec with only one argument. I have added a newline there so it becomes more self-evident.
But your definition says it needs two. One way to fix this is
(define (conc . lsts)
(let rec ([ls lsts]
[acc '()])
(if (empty? ls)
acc
(rec (cdr ls) ; first argument
(append acc (car ls)) ; second argument
))))
Now e.g.
(conc (list 1 2) (list 3 4))
; => '(1 2 3 4)
I used append. Calling list* doesn't seem to do anything useful here, to me.
(edit:)
Using append that way was done for simplicity. Repeatedly appending on the right is actually an anti-pattern, because it leads to quadratic code (referring to its time complexity).
Appending on the left with consequent reversing of the final result is the usual remedy applied to that problem, to get the linear behavior back:
(define (conc2 . lsts)
(let rec ([ls lsts]
[acc '()])
(if (empty? ls)
(reverse acc)
(rec (cdr ls)
(append (reverse (car ls))
acc)))))
This assumes that append reuses its second argument and only creates new list structure for the copy of its first.
The repeated reverses pattern is a bit grating. Trying to make it yet more linear, we get this simple recursive code:
(define (conc3 . lols)
(cond
[(null? lols) empty ]
[(null? (car lols))
(apply conc3 (cdr lols)) ]
[else
(cons (caar lols)
(apply conc3
(cons (cdar lols) (cdr lols))))]))
This would be even better if the "tail recursive modulo cons" optimization was applied by a compiler, or if cons were evaluated lazily.
But we can build the result in the top-down manner ourselves, explicitly, set-cdr!-ing the growing list's last cell. This can be seen in this answer.
I'm trying to print a list of pairs of values (representing key/value pairs) in Racket.
Here's the code I have right now:
#lang racket
(define (write-json keyvalues)
(displayln "{")
(for-each
(lambda (kv) (
(displayln (format "~a: ~a," (car kv) (cdr kv)))))
keyvalues)
(displayln "}"))
(write-json (list (cons "a" 1) (cons "b" 2)))
When I run the example, it prints:
{
a: 1,
Then, it crashes with this error message:
application: not a procedure;
expected a procedure that can be applied to arguments
given: #<void>
arguments...: [none]
context...:
/.../racket/collects/racket/private/map.rkt:58:19: loop
"test.rkt": [running body]
for-loop
run-module-instance!125
perform-require!78
Any idea what's going on?
Thanks!
This is a paren issue. You have an extra set of parentheses around your lambda's body, ie:
( (displayln (format "~a: ~a;" (car kv) (cdr kv))) )
Since displayln is used for side effect, its output is void, hence why your error message states that you're trying to run (#<void>).
In general, whenever you get an error stating "expected a procedure that can be applied to arguments", see if you have parentheses issues in your code block. Editors like Dr. Racket would highlight that region for you.
I have the list of values and want to take first x values from it and create (list (listof first x values) (listof next x values) and so on until this list gets empty...).
For example, given this list: (list "a" "b" "c" "d" "e" "f" "g" "h" "t")
return this: (list (list a" "b" "c") (list "d" "e" "f") (list "g" "h" "t"))
Thanks in advance :)
Remember what a datatype for a list is. Your class is probably doing something like:
;; A IntegerList is one of:
;; - '()
;; - (cons Integer IntegerList)
Given that, your template should reflect this structure. I will solve the base case (where we want to turn a list of integers into lists of one integers.
First I will define a 1List datatype as:
;; a 1List is:
;; - (cons Integer '())
Next, the purpose statement and signature for the function will be:
;; Takes a list of integers and returns a list of 1Lists of the same integers
;; IntegerList -> 1List
(define (make-1list lst)
...)
Okay cool. Now we need test cases:
(check-expect (make-1list (list 1 2 3)) (list (list 1) (list 2) (list 3)))
(check-expect (make-1list (list)) (list))
(check-expect (make-1list (list 42)) (list (list 42)))
Finally, I can make my template:
(define (make-1list lst)
(cond [(null? lst) ...]
[else ... (first lst) ... (rest lst) ...]))
(Note that it sometimes makes sense to make some of the template first, to help you guide what tests you need.)
Finally, we can fill in our code:
(define (make-1list lst)
(cond [(null? lst) '()]
[else (cons (list (first lst)) (make-1list (rest lst)))]))
And finally, are examples are also tests so we just need to run them to make sure everything works.
Now, since you want to make 3Lists instead of 1Lists, do you see how you can follow this recipe to solve the problem?
Write down your data definition.
Make your purpose statement and signature.
Make your examples.
Make your template.
Write the actual function.
Turn your existing examples into tests.
Following this pattern should help you break the problem down into smaller steps. Good luck.
Better way to accomplish this task is to use accumulators & recursion.
How could I recreate the code below with a a list that I had declared
as a variable?
> (map (lambda (i) (string-append i "!"))
(list "peanuts" "popcorn" "crackerjack"))
'("peanuts!" "popcorn!" "crackerjack!")
Thanks
Do you mean like this?
(define (add-! l)
(map (lambda (i)
(string-append i "!")) l))
(define my-list (list "peanuts" "popcorn" "crackerjack"))
(check-expect (add-! my-list)
'("peanuts!" "popcorn!" "crackerjack!"))
Just wrapping your code in a function and calling it on a variable?
I want to auto-generate a bunch of test functions from a list. The advantage being I can change the list (e.g. by reading in a CSV data table) and the program will auto-generate different tests on the next program execution.
For example, say I am trying to identify oxyanions in a string containing a chemical formula.
My list may be something like:
(define *oxyanion-tests*
; name cation
(list (list "aluminate" "Al")
(list "borate" "B")
(list "gallate" "Ga")
(list "germanate" "Ge")
(list "phosphate" "P")
(list "sulfate" "S")
(list "silicate" "Si")
(list "titanate" "Ti")
(list "vanadate" "V")
(list "stannate" "Sn")
(list "carbonate" "C")
(list "molybdate" "Mo")
(list "tungstate" "W")))
I'm reasonably confident that the chemical formula contains one of these oxyanions if there is a cation followed by an oxygen within parentheses (e.g. "(C O3)" ), or if the cation is followed by 2 or more oxygens (e.g. "C O3"). Note that this isn't perfect, since it will miss hypochlorite anions (e.g. "Cl O"), but it's good enough for my application.
(define ((*ate? elem) s-formula)
(or (regexp-match? (regexp (string-append "\\(" elem "[0-9.]* O[0-9.]*\\)")) s-formula)
(regexp-match? (regexp (string-append "(^| )" elem "[0-9.]* O[2-9][0-9.]*")) s-formula)))
I think I need a macro to do this, but I don't really understand how they work from reading the documentation. I'm asking here so that I have a good example to look at that is immediately useful to me.
Here is what I kind of think the macro should look like, but it doesn't work and I don't really have a mental model for figuring out how to fix it.
(require (for-syntax racket))
(define-syntax-rule (define-all/ate? oxyanion-tests)
(for ([test oxyanion-tests])
(match test
[(list name cation) (syntax->datum (syntax (define ((string->symbol (string-append name "?")) s-formula)
((*ate? cation) s-formula))))])))
Thanks for any guidance you can give me!
P.S. Here are a few tests that should pass:
(define-all/ate? *oxyanion-tests*)
(module+ test
(require rackunit)
(check-true (borate? "B O3"))
(check-true (carbonate? "C O3"))
(check-true (silicate? "Si O4")))
I see a couple of errors in your code:
Your *oxyanion-tests* is a runtime value, but you need its values to use as function name identifiers, so it must be available at compile time.
The syntax around the result of syntax-rules is implicit. So with syntax-rules, you only get the macro template language (see the docs for syntax for more info). Thus you can't do the datum->syntax that you are trying to do. You have to use syntax-case instead, which allows you to use all of Racket to compute the syntax objects you want.
Here's what I came up with:
#lang racket
(require (for-syntax racket/syntax)) ; for format-id
(define-for-syntax *oxyanion-tests*
; name cation
(list (list "aluminate" "Al")
(list "borate" "B")
(list "gallate" "Ga")
(list "germanate" "Ge")
(list "phosphate" "P")
(list "sulfate" "S")
(list "silicate" "Si")
(list "titanate" "Ti")
(list "vanadate" "V")
(list "stannate" "Sn")
(list "carbonate" "C")
(list "molybdate" "Mo")
(list "tungstate" "W")))
(define ((*ate? elem) s-formula)
(or (regexp-match?
(regexp (string-append "\\(" elem "[0-9.]* O[0-9.]*\\)"))
s-formula)
(regexp-match?
(regexp (string-append "(^| )" elem "[0-9.]* O[2-9][0-9.]*"))
s-formula)))
(define-syntax (define-all/ate? stx)
(syntax-case stx ()
[(_)
(let ([elem->fn-id
(λ (elem-str)
(format-id
stx "~a?"
(datum->syntax stx (string->symbol elem-str))))])
(with-syntax
([((ate? cation) ...)
(map
(λ (elem+cation)
(define elem (car elem+cation))
(define cation (cadr elem+cation))
(list (elem->fn-id elem) cation))
*oxyanion-tests*)])
#`(begin
(define (ate? sform) ((*ate? cation) sform))
...)))]))
(define-all/ate?)
(module+ test
(require rackunit)
(check-true (borate? "B O3"))
(check-true (carbonate? "C O3"))
(check-true (silicate? "Si O4")))
The key is the elem->fn-id function, which turns a string into a function identifier. It uses datum->syntax with stx as the context, meaning the defined function will be available in the context where the macro is invoked.