How does `let` work in Scheme? - 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 " "))))

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

#<Closure> in output

I was writing a function to switch the last element of a list to the beginning:
(define last-elem
(lambda (l)
(car (reverse l))))
(define all-but-last
(lambda (l)
(reverse (cdr (reverse l)))))
(define (last-to-first x) (append (list last-elem x) (all-but-last x)))
(last-to-first '(1 2 3 4 5 6))
It didn't work and I knew why. I forgot to put the brackets around list last-elem x
The thing is, I was curious about the output of the wrongly-typed code:
(#<Closure> (1 2 3 4 5 6) 1 2 3 4 5)
What is the meaning if this? How did it come out to this?
In Racket, the output is
'(#<procedure:last-elem> (1 2 3 4 5 6) 1 2 3 4 5)
which is a little clearer.
A reference to a function is always stored with its referencing environment, a.k.a. as a closure, and your Scheme implementation chooses to display it that way.
(list last-elem x)
doesn't call the function last-elem. It simply returns a list of two elements: the value of the variable last-elem (which is a procedure) and the value of the argument x. You want:
(list (last-elem x))
But there's no reason to make a list in the first place. Try:
(define (last-to-first x)
(cons (last-elem x) (all-but-last x)))
In Scheme, all identifiers denote either a syntactic keyword (bound to a 'transformer') or a variable (bound to a value). In your code last-elem denotes a variable bound to a function, which you defined. When you write:
(list last-elem x)
the interpreter/compiler produces a list with the value of last-elem and x. Thus, the result of #<Closure> in the list.

Issues with evaluating expressions from user input

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.

Unusual way of parameter passing in Scheme

I'm trying to implement a function in scheme that splits the given list with the function that is also given as parameter to function. To exemplify:
(splitby '("a" "b" "cc" "ab" "abc" "a" "b")
(lambda (x y) (= (string-length x) (string-length y))))
should return (("a" "b") ("cc "ab") ("abc") ("a" "b"))
I'm pretty beginner in Scheme so it is really hard to understand how this 'function like' parameter works, and while implementing such a function what should I do?
In Scheme, functions are objects like numbers, strings, etc. So in this case, your example is equivalent to this:
(define (equal-length x y)
(= (string-length x) (string-length y)))
(splitby '("a" "b" "cc" "ab" "abc" "a" "b") equal-length)
The use of the function is to allow the splitting criterion to be customised. In this case, items are in the same group for as long as the given function returns a true value; otherwise a new group is created.
To get started, write a group-equal function, that groups equal elements together:
(define (group-equal lst)
...)
where, for example,
(group-equal '(1 2 2 3 3 3 4))
returns
((1) (2 2) (3 3 3) (4))
If you successfully implement that, then it's identical to your splitby function, except that you use the given function (equal-length, for example) instead of equal? (as group-equal might use).
Firstly, in Scheme, everything is inside parentheses. So If you want to apply the function f to values x and y, you write:
(f x y)
So you simply need to put splitby inside the first set of parens.
Secondly, functions can be passed as values into other functions, just like data is passed.
So if I have a functions:
(define (double x)
(* x 2))
I can write another function which takes double as an argument:
(define (change_result f x)
(f (+ 3 x)))
; (change_result double 6) returns 18
I can also do this the same way, if I use a lambda (anonymous) function:
(change_result (lambda (x) (* 3 x)) 10)

Resources