Issues with evaluating expressions from user input - scheme

I'm trying to make a recursive definition that reads and execute user expressions, such as (3 + 5). Everything is working, except of one problem with the arithmetic symbol.
I managed to replicate the error in a simpler example:
(define v '(1 + 3))
((cadr v) 2 4)
The (cadr v) is the + symbol, but for some reason the procedure can't be executed on the two arguments that followed. Am I missing something?

I think that's because
(cadr v)
returns '+ not + (literal + not a + function).
You need to evaluate it before applying it to arguments.
This should work:
((eval (cadr v)) 2 4)
^evaluates the '+ to +
edit
This worked in racket in interactive mode.
I'm not really sure what's the difference, but made it work in r5rs mode in racket (a script):
#lang r5rs
;required by r5rs
(define user-initial-environment (scheme-report-environment 5))
(define v '(1 + 2))
;eval expects a quoted expression
;(it seems that if it's a function it has to have arguments too)
;and evaluation environment.
((eval (cadr v) user-initial-environment) 2 4)

As others have pointed out, the problem is that the list you've constructed contains the symbol plus, rather than the function plus.
At its heart, this is the same reason that '(a b) returns a list of two symbols, rather than signalling an unbound identifier error; the quote starts a term in a "data language" where legal identifiers are interpreted as symbols, rather than as variable references.
The question, of course, is what you should do about it. Some here have suggested using 'eval'; this is probably a bad idea, for reasons that I think Matthew Flatt captures elegantly in his blog post on the topic.
Instead, you should probably write a simple mapping function. Here's the way I'd write it. If you use my code in an assignment, be sure to credit me :).
#lang racket
;; a mapping from symbols to operators
(define operator-hash
(hash '+ +
'- -
'* *))
;; ... and whatever other operators you want.
;; example of using it:
(hash-ref operator-hash '+) ;; ==> +

Try this:
(define v '(1 + 3))
(let ((operator (eval (cadr v)))
(operand1 (car v))
(operand2 (caddr v)))
(apply operator (list operand1 operand2)))

You can do it this way with eval in Guile:
(define (infix-eval v)
(eval (list (cadr v)(car v)(caddr v))
(interaction-environment)))
> (infix-eval '(1 + 2))
3
Rather than using interaction-environment, you could supply another environment for evaluation, where you could also define some other symbols not found in standard Scheme, so that expressions like (7 % 3) and (2 ^ 6) would also work.

Related

Define a macro that returns the operator of an expression in Scheme?

I'm looking for functionality like this:
(op (+ 1 2))
; +
I can’t for the life of me seem to figure out how to do this using define-macro. Any help?
Thanks,
Edit:
It's especially confusing cause I can do:
(car '(+ 1 2))
; +
But if I do:
(define-macro (op expr)
(car expr))
(op '(+ 1 2))
It doesn't work.
OP has defined the macro op as if it were a function, but Lisp macros do not work this way. The macro form is evaluated to produce a new form which substitutes for the original macro call, yet the macro arguments are passed into the macro body unevaluated. This means that within the macro body of op, car operates not on the data (+ 1 2), but rather on the data (quote (+ 1 2)).
The goal of the macro op is not to evaluate (car expr), but to produce the form (car expr) (where expr is replaced by the value of the macro argument), which is then evaluated in the REPL after the macro expansion has taken place. One could do this either using list:
(define-macro (opl expr)
(list 'car expr))
or using quasiquotation:
(define-macro (opq expr)
`(car ,expr))
Here, the backquote introduces a template for a list, and the comma causes the symbol expr to be evaluated to its value ((quote (+ 1 2))), and the result inserted into the list. A simple quoted list, e.g. '(car expr) would evaluate to the list (car expr), where expr is just the symbol expr. With quasiquotation, ,expr evaluates to the value of the argument provided in a macro call, e.g. `(car ,expr) --> (car '(+ 1 2)). Note that (list 'car expr) produces the same form when expr is '(+ 1 2), as with (opl '(+ 1 2))
This define-macro syntax is almost identical to the traditional defmacro syntax of Common Lisp, the difference there being that with defmacro the name of the macro goes before a list of formal parameters, e.g. (defmacro op (expr) ;...). define-macro is not available in Standard Scheme, but some Scheme implementations do support it. Guile Scheme supports both defmacro and define-macro. Both of the above macro solutions work in Guile:
scheme#(guile-user)> (opl '(+ 1 2))
$2 = +
scheme#(guile-user)> (opq '(+ 1 2))
$3 = +

evaluating my own functions from list with eval, R5RS

I am having problems with this
e.g. i have
(define (mypow x) (* x x))
and I need to eval expressions from given list. (I am writing a simulator and I get a sequence of commands in a list as an argument)
I have already read that R5RS standard needs to include in function eval as second arg (scheme-report-environment 5), but still I am having issues with this.
This works (with standard function):
(eval '(sqrt 5) (scheme-report-environment 5))
but this does not:
(eval '(mypow 5) (scheme-report-environment 5))
It says:
../../../../../../usr/share/racket/collects/racket/private/kw.rkt:923:25: mypow: undefined;
cannot reference undefined identifier
Eventhough simply called mypow in prompt returns:
#<procedure:mypow>
Any advice, please? (btw I need to use R5RS)
(scheme-report-environment 5) returns all the bindings that are defined in the R5RS Scheme standard and not any of the user defined ones. This is by design. You will never be able to do what you want using this as the second parameter to eval.
The report mentions (interaction-environment) which is optional. Thus you have no guarantee the implementation has it, but it will have all the bindings from (scheme-report-environment 5)
For completeness there is (null-environment 5) which only has the bindings for the syntax. eg. (eval '(lambda (v) "constan) (null-environment 5)) works, but (eval '(lambda (v) (+ 5 v)) (null-environment 5)) won't since + is not in the resulting procedures closure.
Other ways to get things done
Usually you could get away without using eval alltogether. eval should be avoided at almost all costs. The last 16 years I've used eval deliberately in production code twice.
By using a thunk instead of data:
(define todo
(lambda () ; a thunk is just a procedure that takes no arguments
(mypow 5))
(todo) ; ==> result from mypow
Now imagine you have a list of operations you'd like done instead:
(define ops
`((inc . ,(lambda (v) (+ v 1))) ; notice I'm unquoting.
(dec . ,(lambda (v) (- v 1))) ; eg. need the result of the
(square . ,(lambda (v) (* v v))))) ; evaluation
(define todo '(inc square dec))
(define with 5)
;; not standard, but often present as either fold or foldl
;; if not fetch from SRFI-1 https://srfi.schemers.org/srfi-1/srfi-1.html
(fold (lambda (e a)
((cdr (assq e ops)) a))
with
todo)
; ==> 35 ((5+1)^2)-1

Eval not working in Racket

I am using following code modified from Solving Infix Arithmatic in LISP with definition of atom from Check if an argument is a list or an atom to have an infix arithmetic solver:
(define atom? (or/c number? symbol? boolean? string?))
(define (solve expression)
(define templist '())
(println expression)
(if (atom? expression)
expression
(begin
(set! templist (list (second expression)
(solve (first expression))
(solve (third expression)) ))
(println templist)
(eval templist) ) ) )
(solve '(3 + 2))
The output is as follows:
'(3 + 2)
3
2
'(+ 3 2)
+: unbound identifier;
also, no #%app syntax transformer is bound in: +
Hence, the templist is created all right (+ 3 2) but there is error at eval level. Why is '+' seen as an 'unbound identifier'? The eval function otherwise works well on the command line in DrRacket:
> (eval '(+ 3 2))
5
Where is the problem and how can it be solved? Thanks.
Have you not been learning from the answers to your previous questions? Do not use set!, it is not good Scheme/Racket style. It’s especially egregious here because it is doing absolutely nothing at all; take it out and use let (or local, or block with internal definitions, if you’d prefer).
That aside, eval is equally evil, and you don’t need it here. Functions are first class; create a mapping between symbols and functions, then just use the mapping to fetch the relevant function to invoke.
(define (operator->procedure op)
(case op
[(+) +]
[(-) -]
[(*) *]
[(/) /]
[else (error 'operator->procedure "unknown operator ~v" op)]))
(define (solve expression)
(if (atom? expression)
expression
((operator->procedure (second expression))
(solve (first expression))
(solve (third expression)))))
To be even clearer, you could use Racket’s pattern-matching form, match:
(define (solve expression)
(match expression
[(? atom) expression]
[(list a op b)
((operator->procedure op) (solve a) (solve b))]))

DrRacket define begin set! probably should work but dont

Im learning for exam of programming in lisp using DrRacket..
In presentation from lectures i found this code:
(define (f a b c)
(define delta)
(begin
(set! delta (- (* b b) (* 4 a c))
(if (>=? delta 0)
(writeln ”są pierwiastki”)
(writeln ”nie ma pierwiastków)))))
But it dont work.
DrRacket is showing:
. define: bad syntax (missing expression after identifier) in: (define delta)
Can't I set delta value later?
What is the problem?
thanks in advance
The original error message is because
(define delta)
is missing a value. Instead it should be something like:
(define delta 0)
There some other issues:
The double quotes weren't the " character and weren't recognized
by Racket.
Some parens were wrong.
Also I don't know why it was define-ing delta, then immediately
set!-ing it.
I tried to fix/simplify what you posted, and came up with the
following. But I'm not really sure what the function is supposed to
do, so I don't know if the example output is correct.
#lang racket
(define (f a b c)
(define delta (- (* b b) (* 4 a c)))
(if (>= delta 0)
(displayln "są pierwiastki")
(displayln "nie ma pierwiastków")))
;; Example output:
(f 1 2 3)
;;-> nie ma pierwiastków
(f 1 200 3)
;;-> są pierwiastki
Try this:
#lang racket
(define (f a b c)
(define delta 0)
(set! delta (- (* b b) (* 4 a c)))
(if (>= delta 0)
(displayln "są pierwiastki")
(displayln "nie ma pierwiastków")))
What was wrong with your code:
It's probably a copy-paste error, but in Scheme we delimit strings using the " character, not ” as in your code. And the last line is missing the closing ".
Although some interpreters accept a define without an initial value, the standard is that a value must be present after the variable name
A closing parenthesis is missing at the end of the set! line
The define and set! lines can be merged into a single line: (define delta (- (* b b) (* 4 a c)))
The >=? operator is not standard in Scheme, it might work in your interpreter but if possible use the standard >=
The writeln procedure is not standard in Scheme, in Racket you can substitute it for displayln
Not really an error, but you don't need to write a begin inside a procedure definition, it's implicit
In conclusion: the code in the question seems to be intended for a different interpreter, this question was tagged racket but believe me, what you have is not valid Racket code - heck, is not even standard Scheme. Make sure to use the correct interpreter, and be very alert for typos in the text.
I think Greg already has a perfect answer to your problem, but I just want to add the obvious let version of his code as well:
;; for a given ax^2+bx+c=0
;; this displays if it has at least one
;; real number answer or not
(define (equation-has-answer a b c)
(let ((delta (- (* b b) (* 4 a c))))
(if (>= delta 0)
(displayln "Has answer(s)")
(displayln "Has no answers"))))
To just make a predicate you can do this:
;; for a given ax^2+bx+c=0
;; this returns #t if it has at least one
;; real number answer and #f otherwise
(define (equation-has-answer? a b c)
(>= (- (* b b) (* 4 a c)) 0))

How does `let` work in Scheme?

I use let to create a temporary variable, and then use this temporary variable in the next statement. However, DrScheme complained,
let: bad syntax (not an identifier and expression for a binding) in: temp
This is my code snippet:
(define (case-one-helper str)
(let (temp (substring str (+ 3 (string-contains str "my"))))
(substring temp (string-contains temp " "))))
I wonder if the value of variable created by let has to be known at compiled time?
Edit
I've just figured out, missing ().
Thanks,
While not exactly the problem you're experiencing, but an aside based on your questioning about the sequence of evaluating the arguments, let is also "syntactic sugar" for a lambda followed by it's arguments that are first evaluated and then passed to the lambda which is then evaluated.
For instance:
(let ((a (list 1 2 3))
(b (list 4 5 6)))
(cons a b))
is the same as:
((lambda (list-a list-b) (cons list-a list-b)) (list 1 2 3) (list 4 5 6))
So, if you're ever wondering about evaluation sequence, the arguments are evaluated fully before the body is evaluated (and one argument cannot refer to an argument preceding it ... use let* for something that requires bindings like that).
You need to put another set of parentheses around your let declarations:
(define (case-one-helper str)
(let ((temp (substring str (+ 3 (string-contains str "my")))))
(substring temp (string-contains temp " "))))

Resources