Encrypt Scheme: [() is not a pair] - encryption

Define a procedure encrypt that takes three strings: a message to be encrypted and two alphabets, which we will call regular and encrypted. The alphabet strings are both the same length and they do not contain duplicates. For each character in the message, look it up in regular and, if you find it, convert it to the character in the corresponding location of encrypted. For example, if the regular is abc and the encrypted is def, that means that an a in the message will encode as a d, a b encodes as an e, and a c encodes as an f.
i wrote my code as follow:
(define encrypt
(lambda (message regular encrypted)
(define help
(lambda (ls1 ls2 ls3)
(if (null? ls1) '()
(if (and (null? ls2) (null? ls3)) ls1
(if (equal? (car ls1) (car ls2))
(cons (car ls3) (help (cdr ls1) ls2 ls3))
(help ls1 (cdr ls2) (cdr ls3))))))
(list->string (help
(string->list message)
(string->list regular)
(string->list encrypted)))))
I have been trying to get a running. but the result returns Exception in car: () is not a pair
I quite check it many times, but I didn't what I should change. is there anyone can help me?

Óscar López's answer pointed out some of the problems that you might be having in this code, but I think it's important to specifically address the error message that you mentioned: () is not a pair. This means that you're calling a function that expects a pair (so typical candidates would be car and cdr on an empty list. Let's take a look at your code and see where this could happen:
(define help
(lambda (ls1 ls2 ls3)
(if (null? ls1) '() ; a
(if (and (null? ls2) (null? ls3)) ls1 ; b
(if (equal? (car ls1) (car ls2)) ; c
(cons (car ls3) (help (cdr ls1) ls2 ls3)) ; d
(help ls1 (cdr ls2) (cdr ls3)))))) ; e
Lines a and b don't call any functions that expect a pair, so you shouldn't run into this problem there.
In line c, you do (car ls1) and (car ls2). Line a ensured that ls1 isn't (), but ls2 still could be, since b only checked that it's not the case that both ls2 and ls3 are (); either one alone still could be.
In line d, you've got (car ls3) and (cdr ls1). Line a ensured that ls1 isn't (), but ls2 still could be for the same reason given in the previous case.
Line e has (cdr ls2) and (cdr ls3), and both of these could cause a problem, because either one of them (but not both) could be ().
Though your title didn't say it, your question mentioned that this is actually happening with car, which means that it's not happening in e, which leaves c and d. It's either happening in (car ls2) in c or in (car ls3) in d.
If you're using Dr. Racket to run your code, the IDE should highlight the place where the bad call happened (something like what's shown in the screenshot in this answer).

The help function is not doing what you imagine, you actually need two helpers:
One for iterating over the message and "encrypting" each character in it, for that it uses the next function as helper
And one for "encrypting" a single character, that takes care of finding the encrypted character corresponding to a plaintext character
If you don't do the above, you'll discover that not all the characters are being replaced, because you're traversing the regular/encrypted lists only once, but for the algorithm to work you have to traverse them once for each character in the input message.

(define (encrypt message regular encrypted)
(letrec ((key
(lambda (reg enc)
(if (null? reg)
'()
(cons (cons (car reg) (car enc))
(key (cdr reg) (cdr enc))))))
(keys (key (string->list regular)
(string->list encrypted))))
(list->string
(let loop ((message (string->list message)))
(if (null? message)
'()
(cons (cdr (assoc (car message) keys))
(loop (cdr message))))))))

Related

Racket: Compare elements in list recursively

This function is supposed to take a list (full of strings or ints, which is why it starts with that 'if' statement) and check to see if it is in ascending order.
I haven't been able to figure out how to keep it from crashing, as on the last recursive call, the 'cadr' has nothing to pull from, as the 'car' is the final element.
(define (my-sorted? lst)
(if (number? (car lst))
(if (< (car lst) (cadr lst))
(my-sorted? (rest lst))
#f)
#f)
)
I know that the sorted function exists, but I need to implement this function recursively.
Thanks for any help.
The two most basic lists that you should try are:
(my-sorted? '()) ; ==> #t (empty list is always sorted)
(my-sorted? '(1)) ; ==> #t (one element list is always sorted)
Your current code just does car and cadr on its argument so it will fail at different levels with these two tests.
The there is how to compare a number and something which is not a number. < expects both arguments to be numbers, but your list can be both strings and numbers. What is smaller of "x" and 7? You cannot do (< 7 "x").
Instead of nesting if you might consider cond which is lisps way of doing if-elseif-else. Basically you can do your code with cond like this:
(cond
((not (number? (car lst))) #f)
((< (car lst) (cadr lst)) (my-sorted? (rest lst))
(else #f))
EDIT
Since the list should either be all elements as string or all as number you simply can just determine the comparison function by looking at the first element and do your recursion with a named let and reuse the one based on the first element.
(define (my-sorted lst)
;; determine what comparison function to use, bind it to greater?
(define greater?
(if (and (pair? lst) (number? (car lst)))
>
string>?))
;; main recursive loop uses that one function
;; this can be done with define + call as well
(let loop ((lst lst))
(cond ((or (null? lst) (null? (cdr lst))) ...)
((greater? (car lst) (cadr lst)) ...)
(else (loop ...)))))

I'm doing some scheme homework that involves recursive functions and I'm very confused

For my class I need to write a function that accepts a string and add "op" after every consonant found in the string.
What I've got so far is a helper function that checks an individual letter to see if it is a consonant or a vowel. here it is:
(define (check-letter letter)
(if (null? letter)
'empty
(or (char=? letter #\a) (char=? letter #\e)
(char=? letter #\i) (char=? letter #\o)
(char=? letter #\u) (char=? letter #\y))))
So that will give me a true or false for a given letter but I'm not sure how to approach the rest of the problem.
I know I need to use the "string->list" function but I'm very bad with recursive functions.
If anybody could help out and point me in the right direction or somewhere on the internet that might help that would be fantastic
So your initial procedure checks if a char is a vowel. The parameter is a char, no need to check for null here. Also, it's a predicate (returning true or false) so let's call it vowel?:
(define (vowel? letter)
(or (char=? letter #\a) (char=? letter #\e)
(char=? letter #\i) (char=? letter #\o)
(char=? letter #\u) (char=? letter #\y)))
The wrapper function transforming a string to list and back is trivial:
(define (add-op str op)
(list->string
(add-op-list (string->list str) op)))
Now the recursive function, working on a list. You know that a list is constructed as:
`(cons elt1 (cons elt2 (cons elt3 (cons elt4 (.... (cons '() ))))))`
and that recursivity means that
you process the first element (obtained by car), and call the same procedure on the rest of the list (obtained by cdr)
until you reach your base case (here, list is null?, so add the final '())
So this leads to:
(define (add-op-list lst op)
(if (null? lst) ; list is empty: finally add '()
'()
(let ((c (car lst))) ; c is the first element of the list
(if (vowel? c) ; is it a vowel?
(cons c (add-op-list (cdr lst) op)) ; yes, just add it to the resulting list and proces the rest
(cons c (cons op (add-op-list (cdr lst) op))))))) ; no, add c and op
Trying:
> (add-op "Supercalifragilisticexpialidocious" #\!)
"S!up!er!c!al!if!r!ag!il!is!t!ic!ex!p!ial!id!oc!ious!"
Here is a tail-recursive solution (your teacher will surely give you extra-credit for using tail recursion!):
(define (vowel? c)
(member c '(#\a #\e #\i #\o #\u #\y)))
(define (homework-1 string punc)
(let extending ((letters (string->list string)) (result '()))
(cond ((null? letters)
(list->string (reverse result)))
((vowel? (car letters))
(extending (cdr letters)
(cons (car letters) result)))
(else
(extending (cdr letters)
(cons punc (cons (car letters) result))))
> (homework-1 "abc" #\-)
"ab-c-"

Scheme car and cdr recursion

Can someone explain to me how the recursion works in the following function? Specifically, I am interested in what happens when the function reaches its base case. Also, why is a named let used in this code? (I am not familiar with named lets)
(define (unzip list-of-pairs)
(if (null? list-of-pairs)
(cons '() '())
(let ((unzipped (unzip (cdr list-of-pairs))))
(cons (cons (car (car list-of-pairs)) (car unzipped))
(cons (cdr (car list-of-pairs)) (cdr unzipped))))))
The procedure shown doesn't have anything special about it, you're just iterating over a list of this form:
'((1 . 2) (3 . 4) (5 . 6))
The only "weird" part is that the output is building two lists instead of the usual single list. As you know, when we're building a single list as output the base case is this:
(if (null? lst) '() ...)
But here, given that we're simultaneously building two lists, the base case looks like this:
(if (null? lst) (cons '() '()) ...)
The code in the question is not using a named let, it's just a plain old garden-variety let, there's nothing special about it. It's useful because we want to call the recursion only once, given that we need to obtain two values from the recursive call.
If we don't mind being inefficient, the procedure can be written without using let, at the cost of calling the recursion two times at each step:
(define (unzip list-of-pairs)
(if (null? list-of-pairs)
(cons '() '())
(cons (cons (car (car list-of-pairs))
(car (unzip (cdr list-of-pairs))))
(cons (cdr (car list-of-pairs))
(cdr (unzip (cdr list-of-pairs)))))))
Of course, the advantage of using let is that it avoids the double recursive call.

How to remove a given symbol from a list?

I am trying to remove a given symbol from a list.
Here is the code i wrote:
(define member?
(lambda (in-sym in-seq)
(if (and (symbol? in-sym) (sequence? in-seq))
(if (null? in-seq)
'()
(append
(if (equal? in-sym (car in-seq)) '() (list (car in-seq)))
(member? in-sym (cdr in-seq)))))))
It turns out that i remove all occurences of the given symbol although i want to remove only the first occurence. Can somebody help me with this?
You can use a built-in procedure for this, check if your interpreter provides remove:
(remove 'b '(a b b c b))
=> '(a b c b)
Now, if you intend to implement the functionality yourself, I advice you to split the problem in two parts: one procedure that checks if the procedure can be executed (if inSymbol is a symbol and inSeq is a sequence), and the other, remove-member that performs the actual removal of data:
(define member?
(lambda (inSym inSeq)
(if (and (symbol? inSym) (sequence? inSeq)) ; can remove?
(remove-member inSym inSeq) ; then remove!
'can-not-remove))) ; otherwise, present an error message
(define remove-member
(lambda (inSym inSeq)
(cond ((null? inSeq)
'())
((equal? (car inSeq) inSym)
(cdr inSeq))
(else
(cons (car inSeq)
(remove-member inSym (cdr inSeq)))))))
Your problem is that you append to ( member? inSym ( cdr inSeq)) whether you found the symbol or not. What you want to do is this:
(define member?
(lambda (inSym inSeq)
(if (and (symbol? inSym) (sequence? inSeq))
(if (null? inSeq) '()
(if (equal? inSym (car inSeq)) (cdr inSeq)
(append (list (car inSec)) (member? inSym (cdr inSeq)))
)
)
)
)
)
I.e. if you found the symbol, just return (cdr inSeq) instead because you are done.

how to write a reduce-per-key function in scheme?

"define a procedure 'reduce-per-key' which a procedure reducef and a list of associations in which each key is paired with a list. The output is a list of the same structure except that each key is now associated with the result of applying reducef to its associated list"
I've already written 'map-per-key' and 'group-by-key' :
(define (map-per-key mapf lls)
(cond
[(null? lls) '()]
[else (append (mapf (car lls))(map-per-key mapf (cdr lls)))]))
(define (addval kv lls)
(cond
[(null? lls) (list (list (car kv)(cdr kv)))]
[(eq? (caar lls) (car kv))
(cons (list (car kv) (cons (cadr kv) (cadar lls)))(cdr lls))]
[else (cons (car lls)(addval kv (cdr lls)))]))
(define (group-by-key lls)
(cond
[(null? lls) '()]
[else (addval (car lls) (group-by-key (cdr lls)))]))
how would I write the next step, 'reduce-per-key' ? I'm also having trouble determining if it calls for two arguments or three.
so far, I've come up with:
(define (reduce-per-key reducef lls)
(let loop ((val (car lls))
(lls (cdr lls)))
(if (null? lls) val
(loop (reducef val (car lls)) (cdr lls)))))
however, with a test case such as:
(reduce-per-key
(lambda (kv) (list (car kv) (length (cadr kv))))
(group-by-key
(map-per-key (lambda (kv) (list kv kv kv)) xs)))
I receive an incorrect argument count, but when I try to write it with three arguments, I also receive this error. Anyone know what I'm doing wrong?
Your solution is a lot more complicated than it needs to be, and has several errors. In fact, the correct answer is simple enough to make unnecessary the definition of new helper procedures. Try working on this skeleton of a solution, just fill-in the blanks:
(define (reduce-per-key reducef lls)
(if (null? lls) ; If the association list is empty, we're done
<???> ; and we can return the empty list.
(cons (cons <???> ; Otherwise, build a new association with the same key
<???>) ; and the result of mapping `reducef` on the key's value
(reduce-per-key <???> <???>)))) ; pass reducef, advance the recursion
Remember that there's a built-in procedure for mapping a function over a list. Test it like this:
(reduce-per-key (lambda (x) (* x x))
'((x 1 2) (y 3) (z 4 5 6)))
> '((x 1 4) (y 9) (z 16 25 36))
Notice that each association is composed of a key (the car part) and a list as its value (the cdr part). For example:
(define an-association '(x 3 6 9))
(car an-association)
> 'x ; the key
(cdr an-association)
> '(3 6 9) ; the value, it's a list
As a final thought, the name reduce-per-key is a bit misleading, map-per-key would be a lot more appropriate as this procedure can be easily expressed using map ... but that's left as an exercise for the reader.
UPDATE:
Now that you've found a solution, I can suggest a more concise alternative using map:
(define (reduce-per-key reducef lls)
(map (lambda (e) (cons (car e) (map reducef (cdr e))))
lls))

Resources