To start off, this is homework. It isn't my intention to get a complete solution, but I am stuck with how to interpret this problem.
We're basically making an interpreter from a lexer and parser for a language that we have created in class.
Specifically, what I am stuck on is using Scheme/Drracket to implement creating a sequence using the parser that calls the method "make-seq" that can be defined by the following rules:
(make-seq (e)) = e
(make-seq (e1 e2 ... en)) = (make-let '*temp* e1 (make-seq (e2 ... en)))
Where make-let is defined as:
(define (make-let var exp1 exp2)
(list 'let var exp1 exp2))
I've implemented the make-seq method below:
(define (make-seq e)
(if (null? (cdr e))
e
(make-let '*temp* (car e) (make-seq (cdr e)))))
but I am not sure how to represent the 'temp in the rules above. I am fairly certain that I should not have the symbol 'temp actually in the implementation, but I'm not sure what I should be doing there instead.
Could anyone give me pointers as to how I should move forward with this?
Thanks!
Using *temp won't be a problem unless the program it's interpreting also has *test:
(let ((*temp 10))
(seq
expression1
(* *temp *temp))) ; end up using the result from the previous element in the sequence
To fix this you can introduce variables that are ok for the interpreter but not possible to create in code. I've often used symbols and lists where the first element is a special value. Your make-seq creates that instead and your interpreter needs to treat those as symbols too. Usually the interpreter uses eq? so basically every structure created as a list with a tag will be unique for that expansion making each nesting *temp not even bother the other ones created earlier.
Related
Is there normal "set" function (not special form) in Scheme language or some way to implement it?
I'd like to write code something like:
(map (lambda (var)
(set var 0))
'(a b c))
which could assign a value (here it is '0') to variables from list (here they are 'a', 'b' and 'c').
No. And to see why there isn't consider something like this:
(define (mutant var val)
(let ((x 1))
(set var val)
x))
Now, what should (mutant 'x 3) return? If it should return 3, then:
set can't be a function since it needs access to the lexical environment of mutant;
any kind of reasonable compilation of this function is not possible.
If you want set to be a function then catastrophe follows. Consider this definition:
(define (mutant-horror f)
(let ([x 3])
(f)
x))
Now, you would think that this can be optimised to this:
(define (mutant-horror f)
(f)
3)
But it can't. Because you might call it like this:
(mutant-horror (λ () (set 'x 3)))
or, more generally, you might call it with a function which, somewhere eventually in some function called indirectly from it might end up saying (set 'x 3).
This means that no binding can ever be optimised at all, which is a disaster. It's also at least very close to meaning lexical scope is not possible: if as well as set, a function called get exists, which retrieves the binding of a symbol, then you have, essentially, dynamic scope. That in turn makes things like tail-call elimination at least difficult and probably impossible (in fact set probably does this on its own).
Reasons like this are why even very old Lisps, where things like set did exist and did superficially work, actually made special exemptions for compiled code, where set didn't work (see for instance the Lisp 1.5 programmer's manual (PDF link), appendix D. This divergence between the semantics of compiled and interpreted code is one of the things that later Lisps and Lisp-related languages such as CL and Scheme did away with.
If instead you want something like Common Lisp's semantics, where the equivalent thing
(defun mutant (var val)
(let ((x 1))
(set var val)
x))
Would return 1 (unless x was a globally (see below) special variable, in which case it might return something else) and as a side-effect modify the value cell of whatever symbol was named by var (which might be x), then, well, Scheme has no notion of that at all, and that's a good thing on the whole.
Note that a modified version of the function will also work for locally special variables:
(defun mutant/local-special (a b)
(let ((x 1))
(declare (special x))
(set a b)
x))
But in this case you always know there's a special binding happening because you can always see the declaration.
When you write something like
(map (lambda (var)
(set var 0))
'(a b c))
my first thought was that you try to accumulate unordered sets of the form ( (a 0) (b 0) (c 0) ).
You cannot implement your own setter for any of the internal data structures that are provided by the language as this would mean to write a scheme function to modify some data structures that is implemented in C. For the data structures implemented in C you need to provide setters written in C -- supposing the lower language is C.
If you want to implement your own setter you either
-- check how the data structure in implmented, and if it's implemented in scheme you will undestand how to modify it
-- define your own data structure using already existing data structures and define setters for it.
A setter that mutates the data structure contains a ! at the end of its name, such as set!, append!, etc.
A function call just evaluates a series of instructions in an extended environment, the environment in particular is that of definition extended with its function parameters, this is prety much the case in any language...
If you do this:
(define (my-set var val)
(set! var val))
you will bind the value of val to var, but only within the scope of the current call on my-set. The reason you cannot write such function has to do with the nature of scheme itself, var is a pointer to whatever you pass in the function, but set! will make this pointer point to something else (still within the scope of my-set). my-set could work if we had some sort of mechanism of using actual pointers, as some low lever languages allow. But scheme does not...
Note that scheme goes very well with the functional programming style as well as recursion, so if you have a need for a function as you described, you are probably "doing something wrong"... :)
You can, however, do this:
(define my-list (list 1 2 3))
(define (my-set a-list a-value)
(set-car! my-list a-value))
> (my-set my-list 4)
> my-list
(4 2 3)
this works since a-list is a pointer to a cons-cell, set-car! will modify the contents of a cons-cell, but not affect the pointer to which.
I'm having a hard time understanding the syntax of let vs some of the other statements. For example, a "normal" statement has one parentheses:
(+ 2 2)
$2 = 4
Yet the let statement has two:
(let ((x 2)) (+ x 2))
$3 = 4
Why is this so? I find it quite confusing to remember how many parentheses to put around various items.
Firstly, note that let syntax contains two parts, both of which can have zero or more elements. It binds zero or more variables, and evaluates zero or more forms.
All such Lisp forms create a problem: if the elements are represented as a flat list, there is an ambiguity: we don't know where one list ends and the other begins!
(let <var0> <var1> ... <form0> <form1> ...)
For instance, suppose we had this:
(let (a 1) (b 2) (print a) (list b))
What is (print a): is that the variable print being bound to a? Or is it form0 to be evaluated?
Therefore, Lisp constructs like this are almost always designed in such a way that one of the two lists is a single object, or possibly both. In other words: one of these possibilities:
(let <var0> <var1> ... (<form0> <form1> ...))
(let (<var0> <var1> ...) (<form0> <form1> ...))
(let (<var0> <var1> ...) <form0> <form1> ...)
Traditional Lisp has followed the third idea above in the design of let. That idea has the benefit that the pieces of the form are easily and efficiently accessed in an interpreter, compiler or any code that processes code. Given an object L representing let syntax, the variables are easily retrieved as (cadr L) and the body forms as (cddr L).
Now, within this design choice, there is still a bit of design freedom. The variables could follow a structure similar to a property list:
(let (a 1 b 2 c 3) ...)
or they could be enclosed:
(let ((a 1) (b 2) (c 3)) ...)
The second form is traditional. In the Arc dialect of Lisp designed Paul Graham, the former syntax appears.
The traditional form has more parentheses. However, it allows the initialization forms to be omitted: So that is to say if the initial value of a variable is desired to be nil, instead of writing (a nil), you can just write a:
;; These two are equivalent:
(let ((a nil) (b nil) (c)) ...)
(let (a b c) ...)
This is a useful shorthand in the context of a traditional Lisp which uses the symbol nil for the Boolean false and for the empty list. We have compactly defined three variables that are either empty lists or false Booleans by default.
Basically, we can regard the traditional let as being primarily designed to bind a simple list of variables as in (let (a b c) ...) which default to nil. Then, this syntax is extended to support initial values, by optionally replacing a variable var with a (var init) pair, where init is an expression evaluated to specify its initial value.
In any case, thanks to macros, you can have any binding syntax you want. In more than one program I have seen a let1 macro which binds just one variable, and has no parentheses. It is used like this:
(let1 x 2 (+ x 2)) -> 4
In Common Lisp, we can define let1 very easily like this:
(defmacro let1 (var init &rest body)
`(let ((,var ,init)) ,#body))
If we restrict let1 to have a one-form body, we can then write the expression with obsessively few parentheses;
(let1 x 2 + x 2) -> 4
That one is:
(defmacro let1 (var init &rest form)
`(let ((,var ,init)) (,#form)))
Remember that let allows you to bind multiple variables. Each variable binding is of the form (variable value), and you collect all the bindings into a list. So the general form looks like
(let ((var1 value1)
(var2 value2)
(var3 value3)
...)
body)
That's why there are two parentheses around x 2 -- the inner parentheses are for that specific binding, the outer parentheses are for the list of all bindings. It's only confusing because you're only binding one variable, it becomes clearer with multiple variables.
I was a bit surprised by this racket code printing nay when I expected yeah:
(define five 5)
(case 5
[(five) "yeah"]
[else "nay"])
Looking at the racket documentation for case makes it clearer:
The selected clause is the first one with a datum whose quoted form is equal? to the result of val-expr.
So it's about quotation. I'm pretty sure that I did not yet fully grasp what quotation in lisps can buy me. I understand it in the viewpoint of macros and AST transformation. However I'm confused why is it helpful in the case of case for instance..?
I'm also curious, with this specification of case, can I use it to achieve what I wanted to (compare the actual values, not the quoted value), or should I use another construct for that? (cond, while strictly more powerful, is more verbose for simple cases, since you must repeat the predicate at each condition).
The problem is that case introduces implicit quote forms, which cause your example to work for 'five (whose value is 'five), instead of five (whose value is 5).
I almost never use case because of exactly this problem. Instead I use racket's match form with the == pattern:
(define five 5)
(define (f x)
(match x
[(== five) "yeah"]
[_ "nay"]))
(f 5) ; "yeah"
(f 6) ; "nay"
This produces "yeah" on only the value 5, just like you expected. If you wanted it to return "yeah" when it's equal to either five or six, you can use an or pattern:
(define five 5)
(define six 6)
(define (f x)
(match x
[(or (== five) (== six)) "yeah"]
[_ "nay"]))
(f 5) ; "yeah"
(f 6) ; "yeah"
(f 7) ; "nay"
And if you really want to match against quoted datums, you can do that by writing an explicit quote form.
(define (f x)
(match x
[(or 'five 'six) "yeah"]
[_ "nay"]))
(f 5) ; "nay"
(f 6) ; "nay"
(f 7) ; "nay"
(f 'five) ; "yeah"
(f 'six) ; "yeah"
These quote forms are implicit and invisible when you use case, lurking there waiting to cause confusion.
The Racket documentation gives this grammar:
(case val-expr case-clause ...)
where
case-clause = [(datum ...) then-body ...+]
| [else then-body ...+]
Let's compare to your example:
(define five 5)
(case 5 ; (case val-expr
[(five) "yeah"] ; [(datum) then-body1]
[else "nay"]) ; [else then-body2])
We see that (five) is interpreted as (datum). This means that five is
a piece of data (here a symbol), not an expression (later to be evaluated).
Your example of case is evaluated like this:
First the expression 5 is evaluated. The result is the value 5.
Now we look at a clause at a time. The first clause is [(five) "yeah"].
Is the value 5 equal (in the sense of equal?) to one of the datums in (five)? No, so we look at the next clause: [else "nay"]. It is an else-clause so the expression "nay" is evaluated and the result is the value "nay".
The result of the case-expression is thus the value "nay".
Note 1: The left-hand sides of case-clauses are datums (think: they are implicitly quoted).
Note 2: The result of val-expr is compared to the clause datums using equal?. (This is in contrast to Scheme, which uses eqv?.
UPDATE
Why include case? Let's see how one can write the example using cond:
(define five 5)
(let ([val five])
(cond
[(member val '(five)) "yeah"]
[(member val '(six seven)) "yeah"] ; added
[else "nay"])
This shows that one could do without case and just use cond.
However - which version is easier to read?
For a case expression it is easy to see which datums the value is compared to.
Here one must look closely to find the datums. Also in the example we know beforehand that we are trying to find the value among a few list of datums. In general we need to examine a cond-expression more closely to see that's what's happening.
In short: having a case-expression increases readability of your code.
For the historically interested: https://groups.csail.mit.edu/mac/ftpdir/scheme-mail/HTML/rrrs-1986/msg00080.html disussed whether to use eqv? or equal? for case.
UPDATE 2
I'll attempt to given an answer to:
I'm still not clear on the quotation vs working simply on the values though.
I'm wondering specifically why doing the quotation, why working on datum instead
of working on values. Didn't get that bit yet.
Both approaches make sense.
Let's for the sake of argument look at the case where case uses expressions rather than datums in the left hand side of a clause. Also following the Scheme tradition, let's assume eqv? is used for the comparison. Let's call such a
case-expression for ecase (short for expression-case).
The grammar becomes:
(ecase val-expr ecase-clause ...)
where
ecase-clause = [(expr ...) then-body ...+]
| [else then-body ...+]
Your example now becomes:
(define five 5)
(ecase five
[('five) "yeah"]
[else "nay")
This doesn't look too bad and the result is what we are used to.
However consider this example:
(ecase '(3 4)
[('five (list 3 4) "yeah"]
[else "nay")
The result of this would be "nay". The two lists resulting from evaluating the expressions '(3 4) and (list 3 4) are not equal in the sense of eqv?.
This shows that if one chooses to use eqv? for comparisions, having expressions available on the left hand side won't be helpful. The only values that work with eqv? atomic values - and therefore one could just as well use implicit quotations and restrict the left hand side to datums.
Now if equal? was used it would make much more sense to use expressions on the left hand side. The original Racket version of case was the same as the one in Scheme (i.e. it used eq?) later on it was changed to used equal?. If case was designed from scratch, I think, expressions would be allowed rather than datums.
The only remaining issue: Why did the authors of Scheme choose eqv? over equal? for comparisons? My intuition is that the reason were performance (which back in the day was more important than now). The linked to post from the rrrs-authors mailing list gives two options. If you dig a little further you might be able to find responses.
I can't find a reference right now, but case statements use literal, unevaluated data in their different clauses because it is both a frequent use-case and more easily subject to efficient compilation.
You could probably write your own version of Clojure's condp macro or a custom conditional operator to handle your use case.
I saw a question which asked about a contract of a method in a pet language known as let.The language is not important but does contract means that
things that the method takes as an argument and its value after evaluating?
(define extend-env*
(lambda (syms vals old-env)
(if (null? syms)
old-env
(extended-env-record
(car syms)
(car vals)
(extend-env* (cdr syms)
(cdr vals)
old-env)))))
So in here the method takes a symbol a value and an environment and I think it produces a new environment.
Does that mean contract for this method is Identifier(Variable),Value,Environment = Environment ?
Your functions starts like this:
(lambda (syms vals old-env) ...)
Here sym stand for symbol and thus syms stands for a list of syms aka a list of symbols. In the same manner vals stands for a list of values. Finally old-env is an environment.
This covers the input to the function. To confirm that syms is supposed to be a list of symbols, look at how syms is used in the body. We see thee uses: (null? syms), (car syms), and, (cdr syms). This means we guess correctly.
To see type of the output, look for the expression(s) that produce return values.
The simplest is old-env which is an environment. If the function always returns the same type of value, we have determined that the output is an environment. It best to check that the other return expressions also return environments though.
To sum up: the contract seen from Racket is:
extend-env* : list-of-symbols list-of-values environment -> environment
Now in your program the symbols represent identifiers, so you could also write:
extend-env* : list-of-identifiers list-of-values environment -> environment
if you document that identifiers are represented as symbols.
For example,
I want to check if an element is in a list. The algorithm is straightforward, let's do it in C++
bool element_of( const std::vector<int>& lst, int elem ) {
for( int i( 0 ), ie = lst.size(); i < ie; ++i )
if( elem == lst[i] )
return true;
return false;
}
Since Scheme don't let me use single if statement, I can't do something similar to the C++ code above. Then I came up with a temporary variable, namely result. result will have initial value of #f, next I recursively call the function to check the next item in the list i.e. cdr lst ... So my question is, does the variable which created with let restore its initial value each time it enters a new function call or its value stays the same until the last call?
On the other hand, using fold, my solution was,
(define (element-of x lst)
(fold (lambda (elem result)
(if (eq? elem x) (or result #t) result))
#f
lst))
Thanks,
Each Let call creates a new set of variables in the environment that the main body of the Let is being evaluted in. The Let syntax is a "syntactic sugar" for a lambda being evaluated with arguments passed to it that have been evaluted. For instance
(let ((a (func object))
(b (func object2)))
(cons a b))
is the same as writing
((lambda (a b) (cons a b)) (func object) (func object2))
So you can see that in the Let syntax, the arguments are first evaluated, and then the body is evaluated, and the definitions of a and b are utilized in the local environment scope. So if you recursively call Let, each time you enter the body of the Let call, you are evaluating the body in a new environment (because the body is inside a newly defined lambda), and the definition of the arguments defined in the local Let scope will be different (they are actually new variables in a nested environment setup by the new lambda, not simply variables that have been mutated or "re-defined" like you would find in a C++ loop).
Another way of saying this is that you're variables will be like the local scope variables in a C++ recursive function ... for each function's stack-frame, the locally scoped variables will have their own definition, and their own memory location ... they are not mutated variables like you might see in a loop that re-uses the same memory variables in the local scope.
Hope this helps,
Jason
let always reinitialises variables; it's evident, since you must always provide new binding values. e.g.,
(let ((a 42))
...)
Inside the ..., a starts out as 42, always. It doesn't "retain" values from previous invocations.
By the way, I think you meant to write (or result (equal? elem x)) rather than (if (eq? elem x) (or result #t) result). :-)
(or result (equal? elem x)) translates to the following C++ code:
return result || elem == x;
(assuming that the == operator has been overloaded to perform what equal? does, of course.) The benefit of this is that if result is already true, no further comparisons are performed.