Java style FOR loop in a clojure interpeter?
I have a basic interpreter in clojure. Now i need to implement
for (initialisation; finish-test; loop-update) {
statements
}
inside my interpreter. I will attach my interpreter code I got so far. Any help is appreciated.
Interpreter
(declare interpret make-env) ;;
(def do-trace false) ;;
;; simple utilities
(def third ; return third item in a list
(fn [a-list]
(second (rest a-list))))
(def fourth ; return fourth item in a list
(fn [a-list]
(third (rest a-list))))
(def run ; make it easy to test the interpreter
(fn [e]
(println "Processing: " e)
(println "=> " (interpret e (make-env)))))
;; for the environment
(def make-env
(fn []
'()))
(def add-var
(fn [env var val]
(cons (list var val) env)))
(def lookup-var
(fn [env var]
(cond (empty? env) 'error
(= (first (first env)) var) (second (first env))
:else (lookup-var (rest env) var))))
;; -- define numbers
(def is-number?
(fn [expn]
(number? expn)))
(def interpret-number
(fn [expn env]
expn))
;; -- define symbols
(def is-symbol?
(fn [expn]
(symbol? expn)))
(def interpret-symbol
(fn [expn env]
(lookup-var env expn)))
;; -- define boolean
(def is-boolean?
(fn [expn]
(or
(= expn 'true)
(= expn 'false))))
(def interpret-boolean
(fn [expn env]
expn))
;; -- define functions
(def is-function?
(fn [expn]
(and
(list? expn)
(= 3 (count expn))
(= 'lambda (first expn)))))
(def interpret-function
(fn [expn env]
expn))
;; -- define addition
(def is-plus?
(fn [expn]
(and
(list? expn)
(= 3 (count expn))
(= '+ (first expn)))))
(def interpret-plus
(fn [expn env]
(+
(interpret (second expn) env)
(interpret (third expn) env))))
;; -- define subtraction
(def is-minus?
(fn [expn]
(and
(list? expn)
(= 3 (count expn))
(= '- (first expn)))))
(def interpret-minus
(fn [expn env]
(-
(interpret (second expn) env)
(interpret (third expn) env))))
;; -- define multiplication
(def is-times?
(fn [expn]
(and
(list? expn)
(= 3 (count expn))
(= '* (first expn)))))
(def interpret-times
(fn [expn env]
(*
(interpret (second expn) env)
(interpret (third expn) env))))
;; -- define division
(def is-divides?
(fn [expn]
(and
(list? expn)
(= 3 (count expn))
(= '/ (first expn)))))
(def interpret-divides
(fn [expn env]
(/
(interpret (second expn) env)
(interpret (third expn) env))))
;; -- define equals test
(def is-equals?
(fn [expn]
(and
(list? expn)
(= 3 (count expn))
(= '= (first expn)))))
(def interpret-equals
(fn [expn env]
(=
(interpret (second expn) env)
(interpret (third expn) env))))
;; -- define greater-than test
(def is-greater-than?
(fn [expn]
(and
(list? expn)
(= 3 (count expn))
(= '> (first expn)))))
(def interpret-greater-than
(fn [expn env]
(>
(interpret (second expn) env)
(interpret (third expn) env))))
;; -- define not
(def is-not?
(fn [expn]
(and
(list? expn)
(= 2 (count expn))
(= 'not (first expn)))))
(def interpret-not
(fn [expn env]
(not
(interpret (second expn) env))))
;; -- define or
(def is-or?
(fn [expn]
(and
(list? expn)
(= 3 (count expn))
(= 'or (first expn)))))
(def interpret-or
(fn [expn env]
(or
(interpret (second expn) env)
(interpret (third expn) env))))
;; -- define and
(def is-and?
(fn [expn]
(and
(list? expn)
(= 3 (count expn))
(= 'and (first expn)))))
(def interpret-and
(fn [expn env]
(and
(interpret (second expn) env)
(interpret (third expn) env))))
;; -- define with
(def is-with?
(fn [expn]
(and
(list? expn)
(= 3 (count expn))
(= 'with (first expn)))))
(def interpret-with
(fn [expn env]
(interpret (third expn)
(add-var env
(first (second expn))
(interpret (second (second expn)) env)))))
;; -- define if
(def is-if?
(fn [expn]
(and
(list? expn)
(= 4 (count expn))
(= 'if (first expn)))))
(def interpret-if
(fn [expn env]
(cond (interpret (second expn) env) (interpret (third expn) env)
:开发者_如何学Pythonelse (interpret (fourth expn) env))))
;; -- define function-application
(def is-function-application?
(fn [expn env]
(and
(list? expn)
(= 2 (count expn))
(is-function? (interpret (first expn) env)))))
(def interpret-function-application
(fn [expn env]
(let [function (interpret (first expn) env)]
(interpret (third function)
(add-var env
(first (second function))
(interpret (second expn) env))))))
;; the interpreter itself
(def interpret
(fn [expn env]
(cond do-trace (println "Interpret is processing: " expn))
(cond
; basic values
(is-number? expn) (interpret-number expn env)
(is-symbol? expn) (interpret-symbol expn env)
(is-boolean? expn) (interpret-boolean expn env)
(is-function? expn) (interpret-function expn env)
; built-in functions
(is-plus? expn) (interpret-plus expn env)
(is-minus? expn) (interpret-minus expn env)
(is-times? expn) (interpret-times expn env)
(is-divides? expn) (interpret-divides expn env)
(is-equals? expn) (interpret-equals expn env)
(is-greater-than? expn) (interpret-greater-than expn env)
(is-not? expn) (interpret-not expn env)
(is-or? expn) (interpret-or expn env)
(is-and? expn) (interpret-and expn env)
; special syntax
(is-with? expn) (interpret-with expn env)
(is-if? expn) (interpret-if expn env)
; functions
(is-function-application? expn env) (interpret-function-application expn env)
:else 'error)))
I wasn't completely sure what the syntax of your interpreted language is, but it looks like you've implemented lambda
which is all you really need to get a lot of syntactic sugar. You can implement things like for
loops using lambda
.
All you really need to do is add a for-handler
function which parses an expression like:
(for (i 0 (< 1 10))
(print i))
into:
((lambda ()
(define (loop i n)
(if (or (< i n) (= i n))
(begin (print i) (loop (+ 1 i) n))))
(loop 1 10)))
and then passes this new expression to interpret
again. The code above is from my solution to the SICP Exercise 4.9
I didn't spend too much time trying to figure out if your interpreter supports internal defines like this, but it looks like you have with
which may allow you to do something like this:
(with (loop (lambda (i n)
(if (or (< i n) (= i n))
(begin (print i) (loop (+ 1 i) n))))
(loop 1 10))
Essentially, you need to expand the for
expression in an application of a recursive function. The above code defines a function named loop
and immediately calls it. The outer lambda
forces the entire expression to run right away.
If you want some more details on this, there is a chapter in the excellent Structure and Interpretation of Computer Programs on building metacircular interpreters, and there are discussions on implementing internal definitions, as well as discussions what they call "derived expressions" which is basically sugar like for
.
Good luck!
As you may have found, clojure doesn't have for (though you could implement one) loops. You just use a simple recursive call like this:
(loop [var inital-value]
;; statements (regarding "var")
(when-not finish-test
(recur update-var)))
As long as your finish-test
evaluates to false
, recur
will return execution to the start of loop
using the value of update-var
as the new var
. See http://clojure.org/functional_programming
精彩评论