I have the following code in Scheme:
(define (processExpression lst)
(define operand (car lst))
(define operator1 (cadr lst))
(define operator2 (caddr lst))
(if (list? operand)
(begin
(display "Invalid syntax! Erroring out!")
(quit)
)
)
(if (and (number? operator1) (number? operator2))
;The list was of the form (operand c1 c2)
(simplePrefix lst)
)
(if (and (list? operator1) (number? operator2))
(begin
(list operand operator2 (processExpression operator1))
)
)
)
(define (simplePrefix lst)
(let (
(operand (car lst))
(operator1 (cadr lst))
(operator2 (caddr lst)))
(list operator1 operand operator2)
)
)
(display "Please enter a valid s-expression")
(let ((input (read)))
(display (simplePrefix input))
)
This code takes and s-expression and converts it based on some rules. One rule should be that the expression (+ (+ 1 2) 2) should return (+ 2 (1 + 2)).
Currently when I call this code with that expression I get back the result "#undef>" but I have no idea why. The code should call the simplePrefix function on the (+ 1 2) part of the expression and return (1 + 2) but it does not. Does anyone understand why?
The value of
(if condition
something)
is undefined if condition is false, because there's no else part in the if form. In the R5RS version of Scheme, this is stated (emphasis added):
4.1.5 Conditionals
syntax: (if <test> <consequent> <alternate>)
syntax: (if <test> <consequent>)
Syntax: <Test>, <consequent>, and <alternate> may be arbitrary expressions.
Semantics: An if expression is evaluated as follows: first, <test> is evaluated. If it yields a true value (see section
6.3.1), then <consequent> is evaluated and its value(s) is(are) returned. Otherwise <alternate> is evaluated and its value(s)
is(are) returned. If <test> yields a false value and no
<alternate> is specified, then the result of the expression is
unspecified.
(if (> 3 2) 'yes 'no) ===> yes
(if (> 2 3) 'yes 'no) ===> no
(if (> 3 2)
(- 3 2)
(+ 3 2)) ===> 1
Some Scheme-like languages (e.g., Racket) will actually warn you if you try this:
Welcome to Racket v6.1.
> (if #t
(display "hello"))
stdin::1: if: missing an "else" expression
in: (if #t (display "hello"))
context...:
In Common Lisp, the else part is optional, and defined to be nil if not provided:
This is SBCL 1.2.4.58-96f645d, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
* (if t
(print 'hello))
HELLO
HELLO
* (if nil
(print 'hello))
NIL
The expression
(if condition result)
is not equivalent to
if condition
return result
in other languages.
It doesn't return a value, it has a value.
If the condition is false, that value is undefined.
If it is followed by other expressions in a sequence, the value is ignored and the following expressions are evaluated.
The result of evaluating a function application is the value of the last expression in the function's body
(define (foo)
(if (= 1 1)
"one is one")
"I am still here")
> (foo)
"I am still here"
These two added together lead to the error you're experiencing.
Related
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.
Here is an example of an attempt to write a different string to stdout depending on the value of a variable:
(let ((x 1))
(cond ((< x 2)
(display "hooray"))
(else
(display "bummer")))
)
Notice that the correct string does get written, but we also get an "Unspecified return value"
My understanding is that this happens because the interpreter is trying to return the value of the "consequent expression" of the clause as the value of the conditional, but the "display" expression does not return any value.
Is there a better way of doing this?
Given that this expression is evaluated only for its effect (printing a message), you should explicitly return a value (any value!) as the result of the let form:
(let ((x 1))
(cond ((< x 2)
(display "hooray")
(newline))
(else
(display "bummer")
(newline)))
'ok)
Now we'll get:
hooray
'ok
I'm reading The Little Schemer. And thanks to my broken English, I was confused by this paragraph:
(cond ... ) also has the property of not considering all of its
arguments. Because of this property, however, neither (and ... ) nor
(or ... ) can be defined as functions in terms of (cond ... ), though
both (and ... ) and (or ... ) can be expressed as abbreviations of
(cond ... )-expressions:
(and a b) = (cond (a b) (else #f)
and
(or a b) = (cond (a #t) (else (b))
If I understand it correctly, it says (and ...) and (or ...) can be replaced by a (cond ...) expression, but cannot be defined as a function that contains (cond ...). Why is it so? Does it have anything to do with the variant arguments? Thanks.
p.s. I did some searching but only found that (cond ...) ignores the expressions when one of its conditions evaluate to #f.
Imagine you wrote if as a function/procedure rather than a user defined macro/syntax:
;; makes if in terms of cond
(define (my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))
;; example that works
(define (atom? x)
(my-if (not (pair? x))
#t
#f))
;; example that won't work
;; peano arithemtic
(define (add a b)
(my-if (zero? a)
b
(add (- a 1) (+ b 1))))
The problem with my-if is that as a procedure every argument gets evaluated before the procedure body gets executed. thus in atom? the parts (not (pair? x)), #t and #f were evaluated before the body of my-if gets executed.
For the last example means (add (- a 1) (+ b 1)) gets evaluated regardless of what a is, even when a is zero, so the procedure will never end.
You can make your own if with syntax:
(define-syntax my-if
(syntax-rules ()
((my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))))
Now, how you read this is the first part is a template where the predicate consequent and alternative represent unevaluated expressions. It's replaced with the other just reusing the expressions so that:
(my-if (check-something) (display 10) (display 20))
would be replaced with this:
(cond ((check-something) (display 10))
(else (display 20)))
With the procedure version of my-if both 10 and 20 would have been printed. This is how and and or is implemented as well.
You cannot define cond or and or or or if as functions because functions evaluate all their arguments. (You could define some of them as macros).
Read also the famous SICP and Lisp In Small Pieces (original in French).
I'm attempting to write a scheme function that prints each entry of a list on a new line. This function and sample input works as expected, then gives an "illegal function" error and exits. I'm using tinyscheme from the debian repos.
(define print-list
(lambda (l)
(if (null? l)
(display "done\n")
(
(display (car l))
(newline)
(print-list (cdr l))
)
)
)
)
(print-list '(1 2 3 4 5) )
A parenthesis followed by an expression means function application.
(expr1 expr2 ...)
Means evaluate expr1, expr2, .... Then apply the result of expr1 to the result of expr2 ... . If the result of expr1 is not a function, then you'll see the "illegal function" error.
The fix is as leppie states to add a begin:
(define print-list
(lambda (l)
(if (null? l)
(display "done\n")
(begin
(display (car l))
(newline)
(print-list (cdr l))))))
(print-list '(1 2 3 4 5) )
Here
(begin expr1 expr2 ....)
means evalute expr1, expr2, ... in order. Finally return the value of the last expression.
Block Structure
In scheme, the branches of the if special form do not have an implicit block structure. As Soegaard suggests, begin may be used. Or you can use cond to create a blockāin which case, expanding to more than two branches comes along for free.
(define (print-list a-list)
(cond ((null? a-list)(display "done\n"))
(else
(display (car a-list))
(newline)
(print-list (cdr a-list)))))
Problem with Return Type
Note that print-list is problematic because it returns...well something undefined. For example in MIT Scheme:
1 ]=> (print-list '(a b c))
a
b
c
done
;Unspecified return value
Or in Gauche Scheme:
gosh> (print-list '(a b c))
a
b
c
done
#<undef>
While in Racket's implementation of R5RS the return value is not printed.
> (print-list '(a b c))
a
b
c
done
Returning a Value
One common approach to signalling the end of a series of side effects is to return a value. null is one possibility. A symbol is another.
(define (print-list a-list)
(cond ((null? a-list) 'done) ; changed this line.
(else
(display (car a-list))
(newline)
(print-list (cdr a-list)))))
Calling it now will in a Scheme REPL removes the problematic return value:
gosh> (print-list '(a b c))
a
b
c
done
I'm reading The Little Schemer. And thanks to my broken English, I was confused by this paragraph:
(cond ... ) also has the property of not considering all of its
arguments. Because of this property, however, neither (and ... ) nor
(or ... ) can be defined as functions in terms of (cond ... ), though
both (and ... ) and (or ... ) can be expressed as abbreviations of
(cond ... )-expressions:
(and a b) = (cond (a b) (else #f)
and
(or a b) = (cond (a #t) (else (b))
If I understand it correctly, it says (and ...) and (or ...) can be replaced by a (cond ...) expression, but cannot be defined as a function that contains (cond ...). Why is it so? Does it have anything to do with the variant arguments? Thanks.
p.s. I did some searching but only found that (cond ...) ignores the expressions when one of its conditions evaluate to #f.
Imagine you wrote if as a function/procedure rather than a user defined macro/syntax:
;; makes if in terms of cond
(define (my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))
;; example that works
(define (atom? x)
(my-if (not (pair? x))
#t
#f))
;; example that won't work
;; peano arithemtic
(define (add a b)
(my-if (zero? a)
b
(add (- a 1) (+ b 1))))
The problem with my-if is that as a procedure every argument gets evaluated before the procedure body gets executed. thus in atom? the parts (not (pair? x)), #t and #f were evaluated before the body of my-if gets executed.
For the last example means (add (- a 1) (+ b 1)) gets evaluated regardless of what a is, even when a is zero, so the procedure will never end.
You can make your own if with syntax:
(define-syntax my-if
(syntax-rules ()
((my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))))
Now, how you read this is the first part is a template where the predicate consequent and alternative represent unevaluated expressions. It's replaced with the other just reusing the expressions so that:
(my-if (check-something) (display 10) (display 20))
would be replaced with this:
(cond ((check-something) (display 10))
(else (display 20)))
With the procedure version of my-if both 10 and 20 would have been printed. This is how and and or is implemented as well.
You cannot define cond or and or or or if as functions because functions evaluate all their arguments. (You could define some of them as macros).
Read also the famous SICP and Lisp In Small Pieces (original in French).