(define-struct student (first last major age))
(define student1 (make-student "David" "Smith" 'Math 19))
(define student2 (make-student"Joe" "Jones" 'Math 21))
(define student3 (make-student "Eli" "Black" 'Spanish 20))
(define (same-age? s1 s2)
(string=? (student-age s1)
(student-age s2)))
so I am trying to get a boolean as an output if two students are the same age, but when I run it, it says it expects a string as the 1st argument, but given 19. What is the problem?
A couple of your questions are related, you seem to be struggling with comparisons for different data types, here are some pointers:
When comparing numbers, use =
When comparing characters, use char=?
When comparing symbols, use symbol=?
When comparing strings, use string=?
Or simply use equal?, the catch-all procedure that will work for several types and will return true as long as both of its operands are of the same type and equal
For example, all of the following comparisons will return #t:
(equal? 1 1)
(equal? 1.5 1.5)
(equal? #\a #\a)
(equal? 'x 'x)
(equal? "a" "a")
(equal? (list 1 2 3) (list 1 2 3))
You create students with their age fields being integers, not strings (note the lack of double-quotation marks), then try to use string=? function to compare them. You should either use the = function to compare on age:
(define-struct student (first last major age))
(define student1 (make-student "David" "Smith" 'Math 19))
(define student2 (make-student "Joe" "Jones" 'Math 21))
(define student3 (make-student "Eli" "Black" 'Spanish 20))
(define (same-age? s1 s2)
(= (student-age s1)
(student-age s2)))
or create students with their age fields represented as strings:
(define-struct student (first last major age))
(define student1 (make-student "David" "Smith" 'Math "19"))
(define student2 (make-student "Joe" "Jones" 'Math "21"))
(define student3 (make-student "Eli" "Black" 'Spanish "20"))
(define (same-age? s1 s2)
(string=? (student-age s1)
(student-age s2)))
Related
My input data is a hex-formatted string without restriction on the length. I need to process the bytes individually. As an example, for "AABBCCDDEEFF" I want to process AA, then BB, CC, DD, EE, FF.
Using Common Lisp, we can use LOOP:
(loop for (a b) on list do [processing])
In Racket Scheme, I wrote this solution:
(define (split-string str)
(let ((bit #t)
(char-1 null)
(char-2 null)
(result '()))
(for ((char str))
(if bit
(begin
(set! bit #f)
(set! char-1 char))
(begin
(set! bit #t)
(set! char-2 char)
(set! result (cons (~a char-1 char-2) result)))))
;; return
(reverse result)))
(split-string "AABBCCDDEEFF")
;; '("AA" "BB" "CC" "DD" "EE" "FF")
I feel like this is not idiomatic Racket or Scheme code. I wrote a second solution:
(define (split-string2 str)
(bytes->list (integer->integer-bytes (string->number str 16) 8 false)))
(split-string2 "AABBCCDDEEFF")
;; '(255 238 221 204 187 170 0 0)
What is the idiomatic way to perform this kind of operations in Racket Scheme and more generally in Lisp?
There are a bunch of ways of doing this in Racket (as opposed to Scheme more generally): the notions you want are sequences, streams and generators.
First of all a function to compute hex digits from characters (this may exist in Racket, but I was too lazy to find it, and this works):
(define (char->hex-digit c)
;; Turn a character into a hex digit
(cond [(char<=? #\0 c #\9)
(- (char->integer c) (char->integer #\0))]
[(char<=? #\A c #\F)
(+ 10 (- (char->integer c) (char->integer #\A)))]
[(char<=? #\a c #\f)
(+ 10 (- (char->integer c) (char->integer #\a)))]
[else
(error 'char->hex-digit "~A is not a hex character" c)]))
Now here is a simple-minded approach to turning a hex string into a list of bytes which works by creating two sequences from the string, one of which picks out the high and the other the low digit in each pair:
(define (hex-string->byte-list hs)
(for/list ([h (in-string hs 0 #f 2)]
[l (in-string hs 1 #f 2)])
(+ (* (char->hex-digit h) 16) (char->hex-digit l))))
Obviously depending on which variant of for you use you can construct different results.
Examples:
> (hex-string->byte-list "F00D")
'(240 13)
> (hex-string->byte-list "0102030405060708090a0b0C0D0F")
'(1 2 3 4 5 6 7 8 9 10 11 12 13 15)
> (hex-string->byte-list "01xa")
; char->hex-digit: x is not a hex character [,bt for context]
Another approach is to use in-slice:
(define (hex-string->byte-list hs)
(for/list ([hl (in-slice 2 (in-string hs))])
(+ (* (char->hex-digit (first hl)) 16) (char->hex-digit (second hl)))))
And there are lots of other ways of doing this, including creating your own sequence or stream types, so you could write (for/list ([b (in-hex-stream-bytes ...)]) b).
Note that the Common Lisp version would not work since the input is a string; in order to use the same approach you should first convert the string to a list of characters; secondly, you would need to add a by directive to advance over the list by cddr, i.e. skipping the already read b.
That would finally look like:
(loop
for (a b) on (coerce "AABBCCDDEEFF" 'list) by #'cddr
collect (parse-integer (coerce (vector a b) 'string)
:radix 16))
=> (170 187 204 221 238 255)
But, this is a bit wasteful, parse-integer admits :start and :end arguments, so you do not need to allocate any intermediate list or string (apart for the last collect; you could skip it too and just process the value directly):
(loop
with string = "AABBCCDDEEFF"
with size = (length string)
initially (assert (evenp size))
for start from 0 by 2
for end from 2 by 2 upto size
collect (parse-integer string :start start :end end :radix 16))
=> (170 187 204 221 238 255)
One of the idiomatic ways would be to use recursion (preserving the same functionality as your split-string) as follows:
(define (split-string-recur str)
(cond [(or (string=? str "") (string=? "" (substring str 1))) '()]
[else (cons (substring str 0 2) (split-string-recur (substring str 2)))]))
and a tail-recursive version:
(define (split-string-trecur str)
(define (split-string-recur str acc)
(cond [(or (string=? str "") (string=? "" (substring str 1))) acc]
[else (split-string-recur (substring str 2) (append acc (list (substring str 0 2))))]))
(split-string-recur str '()))
The for/list approach with in-slice on in-string sequence (mentioned here) is also an idiomatic approach.
Note that we can also use a small interface on strings like the following to make it more readable:
(module string-util typed/racket
(provide (all-defined-out))
(: empty-string? : (-> String Boolean))
(define (empty-string? s)
(string=? "" s))
(: string-first : (-> String String))
(define (string-first s)
(substring s 0 1))
(: string-last : (-> String String))
(define (string-last s)
(substring s (- (string-length s) 1) (string-length s)))
(: string-rest : (-> String String))
(define (string-rest s)
(substring s 1 (string-length s))))
(require 'string-util)
(define (split-string-recur str)
(cond [(or (empty-string? str) (empty-string? (string-rest str))) '()]
[else (cons (string-append (string-first str) (string-first (string-rest str)))
(split-string-recur (string-rest (string-rest str))))]))
CL-USER 2 > (defun split-string (string)
(loop for i from 0 below (length string) by 2
collect (subseq string i (+ i 2))))
SPLIT-STRING
CL-USER 3 > (split-string "AABBCCDDEEFF")
("AA" "BB" "CC" "DD" "EE" "FF")
CL-USER 4 > (mapcar (lambda (s) (parse-integer s :radix 16)) *)
(170 187 204 221 238 255)
(define-struct school (name students))
;; An SchoolChart is a (make-school Str (listof SchoolChart))
;; names are unique
I have a school chart say
(define s-chart (make-school "Tom" (list
(make-school "James" empty)
(make-school "Claire"
(make-school "David" empty)
(make-school "Travis" empty))
(make-school "Timmy" empty))))
This is a general tree, say I define a function
(define (find-name name school)) ;;produces true if found/false if not.
How do I go about the recursion? This specific case is fine, but each child can have infinite children? I just need a hint
There can only be a finite amount of children.
The amount is arbitrary and only bounded by your machine's memory, but it can't be infinite.
(And your s-chart is ill-formed, since "Claire"'s children are not a list.)
The recursion can be pretty simple.
Here's a depth-first search:
(define (find-name name school)
(or (string=? name (school-name school))
(any (lambda (s) (find-name name s)) (school-students school))))
where (any p ls) is #t if and only if (p e) is #t for at least one element e of the list ls.
Now all that remains is to write any...
Following recursively checks all items and, if found, add the name to a list outside the loop. However, it needs to use set!. It uses string-prefix? instead of string=? for demonstration purposes (to get more names in the current structure):
(define-struct school (name students))
(define s-chart
(make-school "Tom"
(list
(make-school "James" empty)
(make-school "Claire" (list
(make-school "David" empty)
(make-school "Travis" empty)))
(make-school "Timmy" empty))))
(define (find-name name school)
(define ol '())
(let loop ((s school))
(cond
[(list? s)
(when (not(empty? s))
(begin (loop (first s))
(loop (rest s))))]
[else
(when (string-prefix? (school-name s) name)
(set! ol (cons (school-name s) ol)))
(loop (school-students s))
]))
ol
)
(find-name "T" s-chart)
Output:
'("Timmy" "Travis" "Tom")
I'm learning how vectors work.
I have two vectors, one contains names and the other one contains "phone numbers". I wrote a function which is displaying all names and all phone number next to each other:
(define v4 (vector 'Tom 'Michael 'John 'Julia))
(define v5 (vector 2343 1343 2112 372637))
(define db (λ (n)
(cond
((equal? (vector-length v4) n ) "stop" )
(equal? 0 (display (vector-ref v4 n))
(display " ")
(display (vector-ref v5 n ))
(displayln " ")
(set! n (+ n 1))
(db n)
))))
(db 0)
Result is:
Tom 2343
Michael 1343
John 2112
Julia 372637
"stop"
Is this the right way to do it? Is there a better way to do it?
Here's a simpler way in Racket, using iterations and comprehensions and format strings. Arguably, this is more idiomatic and easier to understand:
(define (db v1 v2)
(for ([name (in-vector v1)]
[phone (in-vector v2)])
(printf "~a ~a~n" name phone))
"stop")
Even if we were to write an implementation using only explicit recursion and standard procedures, it's better to avoid using set! to increment the index, pass parameters instead - including the vectors that are going to be iterated, there's no need to refer to global definitions:
(define (db v1 v2)
(let loop ((idx 0))
(cond ((>= idx (vector-length v1)) "stop")
(else
(display (vector-ref v1 idx))
(display " ")
(display (vector-ref v2 idx))
(newline)
(loop (+ idx 1))))))
Either way and assuming that both vectors have the same length, it works as expected:
(define v1 (vector 'Tom 'Michael 'John 'Julia))
(define v2 (vector 2343 1343 2112 372637))
(db v1 v2)
Tom 2343
Michael 1343
John 2112
Julia 372637
"stop"
For reference I am programming in Scheme using DrRacket.
I am trying to count the number of times a name (string) is the first choice in a list of votes, but cannot seem to figure it out.
For some context of the problem,
A vote consists of the names of the three candidates that one person has voted for. This is defined as the structure: (define-struct vote (choice1 choice2 choice3)).
The function top-votes-for is supposed to consume a name and a list of votes and produces the number of times that the given name was the first choice vote in the list of votes.
This is my code (note the definition is incorrect):
;; Data Definition
(define-struct vote (choice1 choice2 choice3))
;; A vote is a structure: (make-vote String Number String).
;; interp. 3 candidates that one person has voted for (String)
(define vote1
(make-vote "Blake" "Joey" "Will"))
(define vote2
(make-vote "Blake" "Bob" "Ash"))
(define vote3
(make-vote "Bob" "Ash" "Blake"))
(define listofVotes
(list vote1 vote2 vote3))
;; Signature: top-votes-for: string list-of-strings -> number
;; Purpose: Consumes a name and a list of votes and produces the number of
;; times that the given name was the first choice vote in the list of votes.
;; Tests:
(check-expect (top-votes-for "Blake" empty) 0)
(check-expect (top-votes-for "Blake" listofVotes) 2)
(check-expect (top-votes-for "Bob" listofVotes) 1)
(check-expect (top-votes-for "Ash" listofVotes) 0)
;; Define:
(define (top-votes-for cand alov)
(cond
[(empty? alov) 0]
[(string=? (vote-choice1 cand) cand) 1]
[else ((first alov) (top-votes-for (rest alov)))]
)
)
Thank you in advance!
Your definition of top-votes-for is wrong. Here's a skeletal version of a corrected solution:
(define (top-votes-for cand alov)
(cond ((empty? alov) 0)
((string=? (vote-choice1 <???>) cand)
(add1 <???>))
(else (top-votes-for cand (rest alov)))))
I've actually given you most of a solution. The rest of it should be easy to figure out, if you understand the code above.
I am trying to do some operation in the list if the predicate returns true. But predicate is given as input in command line and it is a function. Let me give an example.
(define (delete-rows table predicate)
do_something)
And the command line looks like this.
(delete-rows student-table
(lambda (table row)
(eq? (get table row 'name) 'ali)))
=> '(students (name id gpa) (ayse 2 3.7))
Thanks for your help in advance.
Here is a very naïve and inefficient implementation in Racket, just to get you on the right track:
(define (list-index e lst)
(- (length lst) (length (memq e lst))))
(define (get table row col)
(list-ref row (list-index col (second table))))
(define (delete-rows table pred)
(list* (first table)
(second table)
(filter (lambda (r) (not (pred table r))) (cddr table))))
then
(define student-table '(students (name id gpa) (ali 1 2) (ayse 2 3.7) (zalde 3 5)))
(delete-rows student-table (lambda (table row) (eq? (get table row 'name) 'ali)))
=> '(students (name id gpa) (ayse 2 3.7) (zalde 3 5))