Error: contract violation in Scheme - recursion

I'm practicing recursion in Scheme. My code below is used to take a pattern clause and a statement then return a list of bindings if statement match pattern, false if it isn't:
(define remove-question-mark
(lambda (s1)
(if (memq #\? (string->list (symbol->string s1)))
(cdr (string->list (symbol->string s1)))
'())))
(define check-two-symbol
(lambda (symbol1 symbol2)
(if (and (regexp-match #rx#"\\?attrs" (symbol->string symbol1)) (list? symbol2))
(cons symbol1 (list symbol2))
(if (equal? symbol1 symbol2)
'()
(if (equal? (remove-question-mark symbol1) (string->list (symbol->string symbol2)))
(cons symbol1 symbol2)
#f)))))
(define clause
(lambda (state1 state2)
(if (and (null? (car state1)) (null? (car state2)))
'()
(list (check-two-symbol (car state1) (car state2)) (clause (cdr state1) (cdr state2))))))
This is my input:
(clause '(wasDerivedFrom ?der ?e1 ?e2 . ?attrs_derv) '(wasDerivedFrom der e1 e2 (prov:type "Revision")))
I got this error from my code:
car: contract violation
expected: pair?
given: '()
Expected output:
((?der . der)
(?e1 . e1)
(?e2 . e2)
(?attrs_derv (prov:type "Revision")))
After debugging, i found that error happens when (car state1) is ?attrs_derv
If I run function check-two-symbol seperrately, like (check-two-symbol '?attrs-derv '(prov:type "Revision")), it prints out nicely:
(?attrs_derv (prov:type "Revision"))
But with the main function, i get error. So can someone please help me show how to fix this error?? I'm not really familiar with recursion much ....
Thank in advanced!

The error message indicates that you're calling car with something that's not a pair. You only call car in four places, so it's one of those in clause. Now clause is only called from clause, so you don't have to go too far to track this down. What sort of values should clause be called with? When you call clause recursively (last line of the following code), you're calling it with (cdr state1) and (cdr state2). Is there any reason to assume that those aren't '()?
(define clause
(lambda (state1 state2)
(if (and (null? (car state1)) (car (null? state2)))
'()
(list (check-two-symbol (car state1) (car state2))
(clause (cdr state1) (cdr state2))))))
------------ ------------
Let's look at what clause gets called with from your initial input:
(clause '(wasDerivedFrom ?der ?e1 ?e2 . ?attrs_derv) '(wasDerivedFrom der e1 e2 (prov:type "Revision")))
(clause '(?der ?e1 ?e2 . ?attrs_derv) '(der e1 e2 (prov:type "Revision")))
(clause '(?e1 ?e2 . ?attrs_derv) '(e1 e2 (prov:type "Revision")))
(clause '(?e2 . ?attrs_derv) '(e2 (prov:type "Revision")))
(clause '?attrs_derv '((prov:type "Revision")))
At this point you're going to try to call car on ?attrs_derv, and that should cause an error, because a symbol isn't a pair. Indeed, if we simplify clause to the following (so that it doesn't call check-two-symbol)
(define clause
(lambda (state1 state2)
(if (and (null? (car state1)) (car (null? state2)))
'()
(list (clause (cdr state1) (cdr state2))))))
and then try to call the code you mentioned, we get an error:
> (clause '(wasDerivedFrom ?der ?e1 ?e2 . ?attrs_derv) '(wasDerivedFrom der e1 e2 (prov:type "Revision")))
car: contract violation
expected: pair?
given: '?attrs_derv
The fact that you got a different error means that either that call to check-two-symbol makes a call to car somewhere that we don't see, or that the test you showed us didn't line up with the output you showed us. In either case, it looks like you're trying to recurse down two lists in parallel. You're only going to be able to do this if they're both pairs, so you probably want code like
(if (not (and (pair? state1) (pair? state2)))
<then-something>
<else-something>)

Related

Scheme syntax error when recursing

I'm writing a recursive function that will convert an expression from prefix to infix. However, I need to add in a check to make sure part of the input is not already in infix.
For example, I may get input like (+ (1 + 2) 3).
I want to change this to ((1 + 2) + 3)
Here is what I have so far:
(define (finalizePrefixToInfix lst)
;Convert a given s-expression to infix notation
(define operand (car lst))
(define operator1 (cadr lst))
(define operator2 (caddr lst))
(display lst)
(cond
((and (list? lst) (symbol? operand));Is the s-expression a list?
;It was a list. Recusively call the operands of the list and return in infix format
(display "recursing")
(list (finalizePrefixToInfix operator1) operand (finalizePrefixToInfix operator2))
)
(else (display "not recursing") lst);It was not a list. We can not reformat, so return.
)
)
However, this is giving me syntax errors but I cant figure out why. Any help?
You have to check to see if the lst parameter is a list at the very beginning (base case), otherwise car and friends will fail when applied to an atom. Try this:
(define (finalizePrefixToInfix lst)
(cond ((not (pair? lst)) lst)
(else
(define operand (car lst))
(define operator1 (cadr lst))
(define operator2 (caddr lst))
(cond
((symbol? operand)
(list (finalizePrefixToInfix operator1)
operand
(finalizePrefixToInfix operator2)))
(else lst)))))

How should a scheme recursive function return the binding with the specified name in the closest environment

I'm trying to write a scheme recursive function (lookup-env name environment), which returns the binding (pair) with the specified name in an environment (i.e. list of association lists) and null if no such binding is found.
(define (lookup name assoc_list)
(cond
((null? assoc_list) '())
((equal? name (caar assoc_list)) (car assoc_list))
(else (lookup name (cdr assoc_list)))))
(define (lookup-env name environment)
(cond
((null? environment) '())
((equal? name (car (lookup name environment))) (lookup name environment))
(else (lookup name (cdr environment)))))
I should be able to test it out like this
(define l1 '( (ben "short") (cara "walking") (dan "bald")))
(define l2 '( (kurt "is not") (ski "skinny") (kim "cook") (cara "injured")))
(define e (list l1 l2) )
(lookup-env 'ben e)
;Value 14: (ben "short")
(lookup-env 'kurt e)
;Value 15: (kurt "is not")
(lookup-env 'cara e)
;Value 16: (cara "walking")
(lookup-env 'jaga e)
;Value 17: ()
however when i try it i get the error
;The object (), passed as the first argument to car, is not the correct type.
This looks like a follow-up to a previous question. Assuming that the lookup procedure is correctly implemented, there's a mistake in the second condition of the posted code (this was causing the error, when you tried to take the car of an empty list); also in the last case the recursive call is incorrect, you're invoking the wrong procedure. Try this instead:
(define (lookup-env name environment)
(cond
((null? environment) '())
((not (null? (lookup name (car environment))))
(lookup name (car environment)))
(else (lookup-env name (cdr environment)))))
But we can do a bit better, and avoid doing a double lookup in the second condition:
(define (lookup-env name environment)
(if (null? environment)
'()
(let ((binding (lookup name (car environment))))
(if (not (null? binding))
binding
(lookup-env name (cdr environment))))))

assign a list to a local variable in scheme

I'm trying to find a way to assign a list to a local variable, and then add that list to a symbol to make a new list, an improper list. Example below is to illustrate this:
Input: '?e1 ((?e1 ?e0) (?e1 ?ex) (?e1 ?ey) (?e1 ?e2))
Expected output: (?e1 (?e0 ?ex ?ey ?e2))
The idea is that compare (car graph) in all nested list with ?e1, add them together to form a list, assign the list to a variable, finally (list '?e1 (new list))
But actually the recursion make my code fail .....
I have try to write a program, but the output from this is quite ridiculous:
(define agrv '((?e1 ?e0) (?e1 ?ex) (?e1 ?ey) (?e1 ?e2)))
(define successor
(lambda (node graph)
(if (equal? node (car graph))
(cdr graph)
'())))
(define (find-dst node graph)
(if (null? graph)
'()
(let ((custom-list (append (list (successor node (car graph))) (find-dst node (cdr graph)))))
(list node custom-list))))
(find-dst '?e1 agrv)
output is: '(?e1 ((?e0) ?e1 ((?ex) ?e1 ((?ey) ?e1 ((?e2))))))
Can someone please explain where I get wrong??? Thank you very much!!
You might want to get more acquainted with the fundamental Scheme procedures, since your procedure could simply be expressed as follows:
(define (find-dst node graph)
(cons node
(list
(map cadr
(filter (lambda (e) (eq? node (car e))) graph)))))
I would rewrite your example as follows:
(define (find-dst node graph)
(define (sub graph)
(if (null? graph)
null
(let ((e (car graph)))
(if (eq? node (car e))
(cons (cadr e) (sub (cdr graph)))
(sub (cdr graph))))))
(cons node (list (sub graph))))
(find-dst '?e1 '((?e1 ?e0) (?e1 ?ex) (?e1 ?ey) (?e1 ?e2)))
=> '(?e1 (?e0 ?ex ?ey ?e2))
EDIT
Regarding the additional question in your comment, if I understood it correctly, you could do something which is close:
(define (find-dst2 node graph)
(let ((r (find-dst node graph)))
(cons node
(map (lambda (e) (find-dst e graph)) (cadr r)))))
(find-dst2 '?e1 '((?e1 ?e0) (?e0 ?ex) (?e0 ?ey) (?e1 ?e2)))
=> '(?e1 (?e0 (?ex ?ey)) (?e2 ()))

Encrypt Scheme: [() is not a pair]

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))))))))

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.

Resources