Error in case construct with string comparison (scheme) - scheme

(define aaa ;;val:string
(lambda(x) ;;x:string
(case
((string=? (substring x 0 1) "+") (aaa(substring x 1)))
((string=? "a" "b")(string-append("-" (aaa(substring x 1)))))
((char=?(string-ref x 0)#\.) (-404))
(else
(if (= (findpoint x) -1)
"a"
"b"
)
)
)
)
)
Hello, I have a problem with DrRacket:
When I try to run this code, it gives me the error:
case: expected a symbol (without its quote) or a number as a choice, but found a string
referring to the line (5):
((string=? "a" "b")(string-append("-" (aaa(substring x 1)))))
This line was actually supposed to look like this,
((string=? (substring x 0 1) "+")(string-append("-" (aaa(substring x 1)))))
but I thought that using two strings "a" "b" would ease to spot the problem.
I don't understand why i get this error, since both "a" "b" are strings and not symbols or lists, and also I can't understand why I don't get this error on the previous line
CONTEXT:
the procedure is supposed check if the first character of a string is a +/-/., and then do thing through recursion using else (again, "a" "b" are examples)

It seems like you are mixing cond, the flat if-elseif-else, and case, which is similar to a switch statement.
How to use cond:
(cond ((equal? 1 2) 'consequent)
((odd? 1) 'consequent2) ; as many terms as you want. It stops at the first true
(else 'alternative)) ; or else it evaluates the alterntaive
; ==> consequent2
Vs how to use case
(case 'value4
((value1 value2) 'consequent)
((value3 value4) 'consequent2)
(else 'default))
; ==> consequent2
Now that case statement is just sugar. Your Scheme implementation will make it into something similar to this:
(cond ((and (eqv? 'value4 'value1) (eqv? 'value4 'value2)) 'consequent)
((and (eqv? 'value4 'value3) (eqv? 'value4 'value4)) 'consequent2)
(else 'default))
Thus notice the values to match in a case are treated as if they are quoted. Eg. you values can not be variables since they will only match their symbol.
If you want to use case I would have done this:
(define (first-letter-operator? str)
(case (string-ref str 0)
((#\+ #\- #\.) #t)
(else #f)))
(first-letter-operator? "+345634") ; ==> #t
(first-letter-operator? "hello") ; ==> #f

Related

What is Classifier and Transformer in MIT Scheme?

In the following scheme code, accumulate does right-fold. When I tried to run using mit scheme. I ran into following error:
Transformer may not be used as an expression: #[classifier-item 13]
Classifier may not be used as an expression: #[classifier-item 12]
I google searched but didn't find useful information. Is it related a macro?
; This function is copied from SICP chapter 2
(define (accumulate op initial sequence)
(if (null? sequence)
initial
(op (car sequence)
(accumulate op initial (cdr sequence)))))
; works as expected
(accumulate
(lambda (x y) (or x y)) ; replace or with and also works
#f
'(#t #f #t #f #f)
))
; does not work
; error: Classifier may not be used as an expression: #[classifier-item 12]
(accumulate
or
#f
'(#t #f #t #f #f)
))
; does not work
; error: Transformer may not be used as an expression: #[classifier-item 13]
(accumulate
and
#f
'(#t #f #t #f #f)
))
Macros can be passed around in some languages, but not in Scheme and Common Lisp. The reason is that macros should be able to be expanded ahead of time. eg.
(define (cmp a b)
(cond ((< a b) -1)
((> a b) 1)
(else 0)))
Now a compiling Scheme will expand each node recursively replacing it with the expansion until it is no change:
(define (cmp a b)
(if (< a b)
(begin -1)
(cond ((> a b) 1)
(else 0))))
(define (cmp a b)
(if (< a b)
-1
(cond ((> a b) 1)
(else 0))))
(define (cmp a b)
(if (< a b)
-1
(if (> a b)
(begin 1)
(cond (else 0)))))
(define (cmp a b)
(if (< a b)
-1
(if (> a b)
1
(cond (else 0)))))
; end result
(define (cmp a b)
(if (< a b)
-1
(if (> a b)
1
0)))
From this point of cond doesn't need to exist in the underlying language at all since you'll never ever use it, but how would this have to be implemented to work:
(define (test syntax a b)
(syntax a b))
(test or #f #t)
For this to work the underlying language needs to know what or is even after expansion since syntax would need to be bound to or and then the transformation can happen. But when the code runs the macro expansion has already happened and in most implementations you would see something indicating that or is an unbound variable. It seems like MIT Scheme has added error checking for top level syntax syntax that will fire an error if you don't override it. Eg. if you add this you will not see any problems whatsoever:
(define (or a b) (if a a b))
(define (and a b) (if a b #f))
Now after those lines any reference to and and or are not the syntax, but these procedures. There are no reserved words in Scheme so if you do something crazy, like defining define you just cannot use it for the rest f that scope:
(define define display) ; defiens define as a top level variable
(define define) ; prints the representation of the function display
(define test 10) ; fail since test is an undefined variable so it cannot be displayed.
I created a interpreted lisp with macros that actually could be passed, but it isn't very useful and the chances of optimization is greatly reduced.
Yes it's related to the macros / special forms like and and or.
You can make it work simply by wrapping them as lambdas, (accumulate (lambda (a b) (or a b)) ...) -- the results will be correct but of course there won't be any short-circuiting then. The op is a function and functions receive their arguments already evaluated.
Either hide the arguments behind lambdas ((lambda () ...)) and evaluate them manually as needed, or define specific versions each for each macro op, like
(define (accumulate-or initial sequence)
(if (null? sequence)
initial
(or (car sequence)
(accumulate-or initial (cdr sequence)))))
Here sequence will still be evaluated in full before the call to accumulate-or, but at least the accumulate-or won't be working through it even after the result is already known.
If sequence contains some results of heavy computations which you want to avoid in case they aren't needed, look into using "lazy sequences" for that.

Scheme Switch-Statement Syntax

What is the smartest way to create a switch statement in Scheme?
I want to check one value up against several others, if one results true the entire function should result true, otherwise false. I am not very good with the syntax in scheme.
In Scheme you have case:
(case (car '(c d))
((a e i o u) 'vowel)
((w y) 'semivowel)
(else 'consonant)) ; ==> consonant
As you see it compares against literal data. Thus you cannot compare the value with other variables. Then you need cond
An alternative to an explicit comparison agains each value, is to use member:
> (define (vowel? x) (member x '(a e i o u))
> (vowel? 'b)
#f
Base Case
Often if you want to return a boolean value a simple boolean expression will be enough. In the simple case several checks within an or will be enough:
(define (switch val)
(or (equal? val 'some-value)
(equal? val 'some-other-value)
(equal? val 'yet-another-value)))
Higher Order Function
Is we're doing this often it's a lot of work, so we can make a function called make-switch that takes a list of values and returns a function that serves as a switch statement for those values:
(define (make-switch list-of-vals)
(define (custom-switch val)
(define (inner vals)
(cond ((null? vals) #f)
((equal? val (first vals)) #t)
(else
(inner (rest vals)))))
(inner list-of-vals))
Then we can use make-switch like this:
> (define k (make-switch '(1 2 a "b")))
> (k 1)
#t
> (k 5)
#f
> (k "a")
#f
> (k "b")
#t
Faster Lookups
If we're mostly checking against a static set of values, then a hash-table is another alternative. This code in #lang racket shows the general approach, an R5RS Scheme could use SRFI-69:
#lang racket
(define (make-switch alist)
(define (list->hash alist)
(make-hash (map (lambda (x) (cons x x))
alist)))
(lambda (val)
(if (hash-ref (list->hash alist) val #f)
#t
#f)))
Note
There may be cases where you want to use eq? or some other test for equality, but I've left make-custom-make-switch as an exercise for further exploration.

Scheme - logical operator procedures

I know Scheme and other Lisp dialects include logical operators such as 'and' and 'or', but, since I am currently learning Scheme, I am trying to program my own logical operators. So far my attempts at 'or' have been successful (at least so far as my testing has shown me). My logical or operator is the following:
(define (logical-or a b)
(if a true b))
I am trying to program a logical operator for 'and' that also returns a boolean, but I keep getting stuck. I have tried any number of combinations, but I will just list the following one, which doesn't return a boolean:
(define (logical-and a b)
(if a b false))
Any hints or help welcome.
As Uselpa's answer and the leppie's comments mentioned, the operators and and or don't return #t but the value that was not #f that decided the outcome of the form. Thus
(and 'these 'are 'all 'true 'values) ; ==> values
(or 'these 'are 'all 'true 'values) ; ==> these
The logical operators and and or are short circuiting so they are not procedures. Imagine this procedure:
(define (first-true-value lst)
(and (pair? lst)
(or (car lst)
(first-true-value (cdr lst)))))
(first-true-value '()) ; ==> #f
(first-true-value '(#f #f)) ; ==> #f
(first-true-value '(#f #f hello)) ; ==> hello
If you replace and and or with your versions the procedure will never stop evaluating the recursion.
We know we can rewrite and with if. (and) is #t, (and a) is a and (and a b ...) is (if a (and b ...) #f). We could do this with th easiest Scheme macros, syntax-rules:
(define-syntax logical-and
(syntax-rules ()
((logical-and) #t)
((logical-and a) a)
((logical-and a b ...)
(if a (logical-and b ...) #f))))
We can also do or the same way. (or) is #f and (or a b ..) is (if a a (or b ...)):
(define-syntax logical-or
(syntax-rules ()
((logical-or) #f)
((logical-or a b ...) ; NB: zero elements match "b ..."
(if a a (logical-or b ...)))))
There is a problem with this one since it uses a twice.. Try (logical-or (display "hello")). It will evaluate (display "hello") and thus display the text twice. To fix this we need to wrap the value in a let:
(define-syntax logical-or
(syntax-rules ()
((logical-or) #f)
((logical-or a b ...)
(let ((tmp a))
(if tmp
tmp
(logical-or b ...))))))
If you try the same it will only display "hello" once. Lets try writing my initial procedure with the new macros:
(define (first-true-value lst)
(logical-and (pair? lst)
(logical-or (car lst)
(first-true-value (cdr lst)))))
;; and we test them:
(first-true-value '()) ; ==> #f
(first-true-value '(#f #f)) ; ==> #f
(first-true-value '(#f #f hello)) ; ==> hello
Your logical-or does not always return booleans either:
> (logical-or #f 2)
2
As #leppie says, anything not false (#f) is true in Scheme; try experimenting a little with the build-in or function:
> (or 1 2)
1
> (or #f 2)
2
> (or #t 2)
#t
> (or 1 #f)
1
> (or #f #t)
#t
so the definition in Scheme would be:
(define (my-or a b)
(if a a b))
Likewise, for and:
> (and 1 #t)
#t
> (and 1 #f)
#f
> (and 1 2)
2
> (and #f 2)
#f
> (and #t 2)
2
so the definition is
(define (my-and a b)
(if a b a))
If you want to only return booleans, then you'd code
(define (logical-or a b)
(cond
(a #t)
(b #t)
(else #f)))
(define (logical-and a b)
(if a
(if b
#t
#f)
#f))
This works for 2 values, but since the build-in and and or operators allow any number of parameters (even 0) and only evaluate their parameters if necessary the real definitions are a little more complicated.

Scheme: cannot use #t in an if statement

Apologies if the question title is a bit confusing. Maybe after you are through reading it, you can suggest me a better title.
As a part of a homework assignment for an online course, I wrote an iterative procedure in mit-scheme to display number from 1 to a given number.
The code below works fine:
(define (count2-iter num)
(define (iter counter)
(cond ((> counter num) #t)
(else (display counter)
(iter (+ counter 1)))))
(iter 1))
The output:
1 ]=> (load "count2-iter.scm")
;Loading "count2-iter.scm"... done
;Value: count2-iter
1 ]=> (count2-iter 10)
12345678910
;Value: #t
Personally I do not like using cond for 2 branches only and I tried to use if for this.
(define (count2-iter1 num)
(define (loop idx)
(if (> idx num)
#t
((display idx)
(loop (+ idx 1)))))
(loop 1))
The output:
1 ]=> (count2-iter1 5)
5
;The object #!unspecific is not applicable.
;To continue, call RESTART with an option number:
; (RESTART 2) => Specify a procedure to use in its place.
; (RESTART 1) => Return to read-eval-print level 1.
Why is this? Shouldn't #t be evaluated the same way it was used in cond? Would really appreciate an explanation as I am still new to Scheme.
Try this instead:
(define (count2-iter1 num)
(define (loop idx)
(if (> idx num)
#t
(begin ; notice the difference!
(display idx)
(loop (+ idx 1)))))
(loop 1))
Here's why: when you use an if, there can be only one expression in the consequent part and one in the alternative part. If more than one expression is needed, we have to surround them with a (begin ...). You surrounded the expressions between (...), which is not ok, because parenthesis are used for function application (that's why the error message states that The object #!unspecific is not applicable).
On the other hand, a cond has an implicit begin for each of its clauses when a condition is met. Personally, I prefer to stick with using cond when I need more than one expression after a condition - it's less verbose.

Scheme - eq? compare between 2 strings?

I have a problem in my program.
I have a condition that compare between 2 string:
(if (eq? (exp1) (exp2)))
When exp1 give me a string, and exp2 give me a string. To be sure, when I change the "eq?" to "=", it give me the next problem:
=: expects type <number> as 2nd
argument, given: ie; other arguments
were: ie.
When I'm running the program, the function doesnt enter to the first expression in the "if" function, and enter to the second one (meaning the condition is false).
What can I do?
Thank you.
According to the Equivalence predicates section of R6RS, you should be using equal?, not eq?, which instead tests whether its two arguments are exactly the same object (not two objects with the same value).
(eq? "a" "a") ; unspecified
(equal? "abc" "abc") ; #t
As knivil notes in a comment, the Strings section also mentions string=?, specifically for string comparisons, which probably avoids doing a type check.
I wrote a little helper function for this problem.
; test if eq?
(define ==
(lambda (x y)
(if (and (string? x) (string? y))
(string=? x y)
(if (or (string? x) (string? y))
(= 1 0) ;return false
(equal? x y)))))
(define a "aString")
(define l '("aString" "aOtherString"))
(== (car l) a) ; true
(== 1 1) ; true
(== 1 0) ; false
(== "a" 1) ; false diff. type
(== "a" "b") ; false
(== "a" "a") ; true
(== '("a" "b") '("a" "b"))

Resources