(use '[clojure.core.match :only [match]]) (defn evaluate [env [sym x y]] (match [sym] ['Number] x ['Add] (+ (evaluate env x) (evaluate env y)) ['Multiply] (* (evaluate env x) (evaluate env y)) ['Variable] (env x))) (def environment {"a" 3, "b" 4, "c" 5}) (def expression-tree '(Add (Variable "a") (Multiply (Number 2) (Variable "b")))) (def result (evaluate environment expression-tree))It boils down to defining 4 types of expressions, each of which receives subexpressions and the environment and later passes the environment to the subexpressions. Easy, right? There are also a few solutions that avoid passing the environment. Instead they have 'evaluate' function that preprocesses the expression tree and later evaluates it without the environment at all. For example they replace variables with their values upfront.
And then I saw this:
import Data.Map import Control.Monad number = return add = liftM2 (+) multiply = liftM2 (*) variable = findWithDefault 0 environment = fromList [("a",3), ("b",4), ("c",7)] expressionTree = add (variable "a") (multiply (number 2) (variable "b")) result = expressionTree environmentAnd I was wondering how does it work. Where the hell is environment passing or 'evaluate' function?! Can you write such code? If not, it's time to learn monads.
1 komentarze :
This becomes much easier to understand if the types are explicit. Then it's obvious that you're dealing with the reader monad.
import Data.Map
import Control.Monad
type Env = Map String Integer
type ReadEnv a = Env -> a
number :: a -> ReadEnv a
number = return
add, multiply :: Num a => ReadEnv a -> ReadEnv a -> ReadEnv a
add = liftM2 (+)
multiply = liftM2 (*)
variable :: String -> ReadEnv Integer
variable = findWithDefault 0
environment :: Env
environment = fromList [("a",3), ("b",4), ("c",7)]
expressionTree :: ReadEnv Integer
expressionTree = add (variable "a") (multiply (number 2) (variable "b"))
result :: Integer
result = expressionTree environment
main = print result
Post a Comment