What is the algorithm used by programming languages to eval ASTs? [closed] - lisp

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
What is the algorithm used by programming languages to eval their ASTs?
That is, suppose we have 4 basic functions, /*+-. What is a basic algorithm that will correctly eval any AST in the form of, for example:
(+ (- (* 3 2) (+ (/ 5 2))) (* 2 4))
My doubt is actually what happens if the evaluation of a node returns something that still have to be evaluated. For example, in Scheme, the evaluation of ((lambda (a) (+ a 2)) 3) would be (+ 3 2). But this could be evaluated again into 5. So how does the language determine when to stop evaluating a form?

You're totally misunderstanding how Scheme/Lisp evaluation works. I'll use the example you gave:
(+ (- (* 3 2) (+ (/ 5 2))) (* 2 4))
To evaluate a list, we evaluate each of the elements. The first is expected to return a procedure (I'm ignoring the special case of syntax operators), the rest can return arbitrary values. We call the procedure with the rest as arguments.
At the top level, this is a list of 3 elements:
+
(- (* 3 2) (+ (/ 5 2)))
(* 2 4)
Each of these is evaluated. The first is a variable whose value is a procedure (Scheme's built-in addition function). The others, being lists, require recursion into the evaluation algorithm. I'm going to skip describing the second one, because of its complexity, and go to the third: (* 2 4).
This is a list of 3 elements: *, 2, and 4. As above, * is the multiplication function. 2 and 4 are literals, so they evaluate to themselves. So we call the multiplication function with the arguments 2 and 4, and it returns 8.
The complicated second argument goes through the same process, just with several more levels of recursion. It eventually returns 4. So we then call the multiplication function with the arguments 4 and 8, and it returns 32.
Your second example is processed similarly. At the top, you have a list of two elements:
(lambda (a) (+ a 2))
3
Each of these is evaluated. Lambda is special syntax that parses its contents and returns a procedure that evaluates its body in a context where the parameter variables are bound to arguments, so the first returns a procedure that adds 2 to its argument and returns that. 3 is a literal, so it just returns the number 3. We then call the procedure with the argument 3, it adds 2 to it and returns 5.

In the case you give, the execution will stop at 5, since it is a literal value and represents itself. This is not hard to test for. You might as well ask how a function that traverses a list in depth knows how to stop (in fact, you should, since in Scheme this is the same thing).
In Scheme, any compound expression should eventually resolve to one of the 7 primitive datatypes or the empty list, unless it becomes trapped in an infinite loop. If you want to know in advance if the expression will resolve, well, that's an interesting problem: http://en.wikipedia.org/wiki/Halting_problem

I think you may be asking the wrong question, but I will try:
Until it gets a result that it can work with. In your example you're asking about when an interpeter stops evaluating an expression... its 100% language depedent and would be a completely different answer if you were to ask about a compiler. For your Scheme example, you would need to read the Scheme specification (R5RS).
So it is defined by the writer of the interpreter. If a single literal (or even variable) is the expected result of an expression in my language, then it would stop there.

There are many different algorithms.
Alternative 1: You could compile the AST to an intermediate representation which is more linear. Your code could be compiled to something like the following:
a <- 3 * 2
b <- 5 / 2
c <- a - b
d <- 2 * 4
e <- c + d
return e
This is easy to evaluate, since it is just a sequence of instructions. Most of the instructions have the same format: X <- Y OP Z, so the evaluator will be very simple.
Alternative 2: You can compile alternative #1 to machine code or byte code.
li r3, 3
muli r3, 2
li r4, 5
divi r4, r5, 2
subf r3, r3, r4
li r4, 2
muli r4, r4, 4
add r3, r3, r4
blr
Alternative 3: You can compile alternative #1 to a special form called SSA, or "single static assignment", which is similar to #1 but the LHS of every assignment is unique, and special "phi" nodes are used to combine values from different branches. SSA can then be compiled to machine code or byte code.
Alternative 4: You can evaluate the AST by recursive descent. This is covered thoroughly in most books on Scheme / Lisp.
Alternative 5: You can use recursive descent to convert the code to stack machine code, and then evaluate that. Something like:
push 3
push 2
mul
push 5
push 2
div
sub
push 2
push 4
mul
add
ret
Alternative ∞: There are plenty of other techniques. The books written on this subject are thick.

Related

Lisp, calculating mixed expressions

i'am currently trying lisp and i need to solve a Problem. I want to write a function that takes a list as input and returns the calculated number. The elements that are not numbers should be added at the end of the list. Its important that the caluclated number is in front. At the end of this post you can see some function calls with the output i'am looking for.
(Function '(+ 100 1 2 3 4 5 6));-> 121
(Function '(+ 1 2 A B 3 C));-> (+ 6 C B A)
(Function '(+ A B C D 0));-> (+ D C B A)
(Function '(- 2 3 4 5 6));-> -19
(Function '(- 1 B));-> (- 1 B)
(Function '(- 6 2 A B 3 C));-> (- 1 C B A)
In any Lisp you have car to fetch the first element of a list and cdr for fetch the list without the first element and cons to add a list to the beginning of another list.
identify the operation by looking at the first element. Since - in a quoted list is a symbol you cannot apply this so you need to have a list of acceptable operations.
Loop through the rest of the list accumulating the non numbers to a list and the numbers using the operation.
cons the resulting number onto the list of symbols, then the operation making it (op num . symbols)
I'm assuming that + and - don't work as math as swithcing the order when using - or any other non associative operation changes the result.

coerce function in common lisp -- arrays and lists

I am seeing different behavior of coerce between different versions of Common Lisp - wondering which one is "right" or is the standard ambiguous on this seemingly simple question:
is
(coerce '(1 2 3) 'array)
correct lisp? It works fine in Clozure Common Lisp but not in sbcl.
And when it does not work what is the easiest way to coerce a list into an array?
Thanks
The specification says:
If the result-type is a recognizable subtype of vector, and the object is a sequence, then the result is a vector that has the same elements as object.
array is not a subtype of vector -- vectors are 1-dimensional arrays, but array includes arrays with any number of dimensions.
You can use one of these
(coerce '(1 2 3) 'vector)
(coerce '(1 2 3) '(array t (*)))
In the second version, (*) specifies a single dimension whose size is unspecified.
Your use is unspecified, so implementations are free to implement it as they please. If it returns a value, the value has to be an ARRAY of some kind.
To add to Barmar's answer (this is really a comment, but it's too long), while it's fine for CCL to do what it does, I think it's clear that something like this would be very hard to define in a standard.
Consider something like this:
(coerce '((1 2 3) (4 5 6) (7 8 9)) 'array)
What is the result of this meant to be? Should it be:
a vector each of whose elements is a three-element list?
the equivalent of (make-array '(3 3) :initial-contents '((1 2 3) (4 5 6) (7 8 9)))?
the transpose of that array?
I think either of the first two are reasonable interpretations: sometimes you will want one, sometimes the other. The third is probably not reasonable given CL is a row-major language.
So if (coerce ... 'array) were in the standard, how would you specify which of these you wanted? If you just chose one, which should it be (and how do you now reach agreement with the people on the committee who think it should be the other!)?

What is the difference between Clojure clojure.core.reducers/fold and Scala fold?

I came across that Clojure has clojure.core.reducers/fold function.
Also Scala has built-in fold function but could not understand that they are working differently or not?
I assume that you are talking about clojure.core.reducers/fold.
Scala's default fold implementation on sequences is very simple:
collection.fold(identityElem)(binOp)
simply starts with the identityElem and then traverses the collection sequentially, and applies the binary operation binOp to the already accumulated result and the current sequence value, e.g.
(1 to 3).fold(42000)(_ + _)
will result in 42000 + 1 + 2 + 3 = 42006.
Clojure's fold with the full signature
(r/fold n combinef reducef coll)
from the above mentioned package works in parallel in two stages. First, it splits the input into smaller groups of size n (approximately), then reduces each group using reducef, and finally combines the results of each group using combinef.
The main difference is that combinef is expected to be both a zeroary and binary at the same time (Clojure has multi-ary functions), and (combinef) (without arguments) will be invoked to produce identity elements for each partition (thus, this documentation is correct, and this documentation lies).
That is, in order to simulate Scala's fold from the above example, one would have to write something like this:
(require '[clojure.core.reducers :as r])
(r/fold 3 (fn ([] 42000) ([x y] y)) + [1 2 3])
And in general, Scala's fold
collection.fold(identityElement)(binOp)
can be emulated by reducers/fold as follows:
(r/fold collectionSize (fn ([] identityElem) ([x y] y)) binOp collection)
(note the ([x y] y) contraption that throws away the first argument, it's intentional).
I guess the interface wasn't intended to be used with any zero-binary operations that are not monoids, that's the reason why Scala's fold is so awkward to simulate using Clojure's fold. If you want something that behaves like Scala's fold, use reduce in Clojure.
EDIT
Oh, wait. The documentation actually states that
combinef must be associative, and, when called with no
arguments, (combinef) must produce its identity element
that is, we are actually forced to use a monoid as the combinef, so the above 42000, ([x y] y)-example is actually invalid, and the behavior is actually undefined. The fact that I somehow got the 42006 out was a hack in the strictly technical sense that it relied on undefined behavior of a library function to obtain the desired result 42006.
Taking this extra information into account, I'm not sure whether Scala's fold can be simulated by Clojure's core.reducers/fold at all. Clojure's fold seems to be constrained to reductions with a monoid, whereas Scala's fold is closer to the general List catamorphism, at the expense of parallelism.
The clojure.core.reducers namespace is a specialized implementation designed for parallel processing of large datasets. You can find full docs here:
https://clojure.org/reference/reducers.
(r/fold reducef coll)
(r/fold combinef reducef coll)
(r/fold n combinef reducef coll)
r/fold takes a reducible collection and partitions it into groups of
approximately n (default 512) elements. Each group is reduced using
the reducef function. The reducef function will be called with no
arguments to produce an identity value in each partition. The results
of those reductions are then reduced with the combinef (defaults to
reducef) function. When called with no arguments, (combinef) must
produce its identity element - this will be called multiple times.
Operations may be performed in parallel. Results will preserve order.
Until you are maxing out your machine, you should just stick to the basic reduce function:
https://clojuredocs.org/clojure.core/reduce
This is essentially the same as Scala's fold function:
(reduce + 0 [1 2 3 4 5]) => 15
where the function signature is:
(reduce <op> <init-val> <collection-to-be-reduced> )

How to apply lambda calculus rules in Racket?

I am trying to test some of the lambda calculus functions that I wrote using Racket but not having much luck with the testcases. For example given a definition
; successor function
(define my_succ (λ (one)
(λ (two)
(λ (three)
(two ((one two) three))))))
I am trying to apply it to 1 2 3, expecting the successor of 2 to be 3 by doing
(((my_succ 1) 2) 3)
logic being that since my_succ is a function that takes one arg and passes it to another function that takes one arg which passes it to the third function that takes one arg. But I get
application: not a procedure;
expected a procedure that can be applied to arguments
given: 1
arguments.:
I tried Googling and found a lot of code for the rules, but no examples of application of these rules. How should I call the above successor function in order to test it?
You are mixing two completely different things: lambda terms and functions in Racket.
In Racket you can have anonymous functions, that can be written in the λ notation (like (λ(x) (+ x 1)) that returns the successor of an integer, so that ((λ(x) (+ x 1)) 1) returns 2),
in Pure Lambda Calculus you have only lambda terms, that are written with in a similar notation, and that can be interpreted as functions.
In the second domain, you do not have natural numbers like 0, 1, 2, ..., but you have only lambda terms, and represent numbers as such. For instance, if you use the so-called Church numerals, you represent (in technical term encode) the number 0 with the lambda term λf.λx.x, 1 with λf.λx.f x, 2 with λf.λx.f (f x) and so on.
So, the function successor (for numbers represented with this encoding) correspond to a term which, in Racket notation, is the function that you have written, but you cannot apply it to numbers like 0, 1, etc., but only to other lambda expressions, that is you could write something like this:
(define zero (λ(f) (λ (x) x))) ; this correspond to λf.λx.x
(successor zero)
The result in Racket is a procedure (it will be printed as: #<procedure>), but if you try to test that your result is correct, comparing it with the functional encoding of 1, you will find something strange. In fact:
(equal? (successor zero) (λ(f) (λ(x) (f x))))
produces #f, since if you compare two procedures in Racket you obtain always false (e.g. (equal? (λ(x)x) (λ(x)x)) produces #f), unless you compare the “identical” (in the sense of “same memory cell”) value ((equal? zero zero) gives #t). This is due to the fact that, for comparing correctly two functions, you should compare infinite sets of couples (input, output)!
Another possibility would be representing lambda terms as some kind of structure in Racket, so you can represent Church numerals, as well as "normal" lambda terms, and define a function apply (or better reduce) the perform lambda-reduction.
You are trying to apply currying.
(define my_succ
(lambda(x)(
lambda(y)(
lambda(z)(
(f x y z)))))
(define (add x y z)
(+ x y z))
((( (my_succ add)1)2)3)
Implementation in DR Racket:

How to define a function that returns half of input, in two different ways?

I am reading a Gentle Introduction to Symbolic Computation and it asks this question. Basically, the previous content deals with making up bigger functions with small ones. (Like 2- will be made of two 1- (decrement operators for lisp))
So one of the questions is what are the two different ways to define a function HALF which returns one half of its input. I have been able to come up with the obvious one (dividing number by 2) but then get stuck. I was thinking of subtracting HALF of the number from itself to get half but then the first half also has to be calculated...(I don't think the author intended to introduce recursion so soon in the book, so I am most probably wrong).
So my question is what is the other way? And are there only two ways?
EDIT : Example HALF(5) gives 2.5
P.S - the book deals with teaching LISP of which I know nothing about but apparently has a specific bent towards using smaller blocks to build bigger ones, so please try to answer using that approach.
P.P.S - I found this so far, but it is on a completely different topic - How to define that float is half of the number?
Pdf of book available here - http://www.cs.cmu.edu/~dst/LispBook/book.pdf (ctrl+f "two different ways")
It's seems to be you are describing peano arithmetic. In practice it works the same way as doing computation with fluids using cups and buckets.
You add by taking cups from the source(s) to a target bucket until the source(s) is empty. Multiplication and division is just advanced adding and substraction. To halve you take from source to two buckets in alterations until the source is empty. Of course this will either do ceil or floor depending on what bucket you choose to use as answer.
(defun halve (x)
;; make an auxillary procedure to do the job
(labels ((loop (x even acc)
(if (zerop x)
(if even (+ acc 0.5) acc)
(loop (- x 1) (not even) (if even (+ acc 1) acc)))))
;; use the auxillary procedure
(loop x nil 0)))
Originally i provided a Scheme version (since you just tagged lisp)
(define (halve x)
(let loop ((x x) (even #f) (acc 0))
(if (zero? x)
(if even (+ acc 0.5) acc)
(loop (- x 1) (not even) (if even (+ acc 1) acc)))))
Edit: Okay, lets see if I can describe this step by step. I'll break the function into multiple lines also.
(defun half (n)
;Takes integer n, returns half of n
(+
(ash n -1) ;Line A
(if (= (mod n 2) 1) .5 0))) ;Line B
So this whole function is an addition problem. It is simply adding two numbers, but to calculate the values of those two numbers requires additional function calls within the "+" function.
Line A: This performs a bit-shift on n. The -1 tells the function to shift n to the right one bit. To explain this we'll have to look at bit strings.
Suppose we have the number 8, represented in binary. Then we shift it one to the right.
1000| --> 100|0
The vertical bar is the end of the number. When we shift one to the right, the rightmost bit pops off and is not part of the number, leaving us with 100. This is the binary for 4.
We get the same value, however if we perform the shift on nine:
1001| --> 100|1
Once, again we get the value 4. We can see from this example that bit-shifting truncates the value and we need some way to account for the lost .5 on odd numbers, which is where Line B comes in.
Line B: First this line tests to see if n is even or odd. It does this by using the modulus operation, which returns the remainder of a division problem. In our case, the function call is (mod n 2), which returns the remainder of n divided by 2. If n is even, this will return 0, if it is odd, it will return 1.
Something that might be tripping you up is the lisp "=" function. It takes a conditional as its first parameter. The next parameter is the value the "=" function returns if the conditional is true, and the final parameter is what to return if the conditional is false.
So, in this case, we test to see if (mod n 2) is equal to one, which means we are testing to see if n is odd. If it is odd, we add .5 to our value from Line A, if it is not odd, we add nothing (0) to our value from Line A.