2014-05-22 88 views
0

我觉得很难学秒差距在Haskell,所以我试图让我的大学项目(即分析文件与表格字符串变量名哈斯克尔

x=3 
y=4 
z=x+y 
badluck=(x+sqrt(z)*7) 

我好不容易写一个函数解析器从文件中获取所有内容并验证文件,并且我被困在使x成为变量名的情况下,我知道在javascript中它是eval,但我在Haskell中找不到任何类似的东西。直到现在:

ischarorscore :: Char -> Bool 
ischarorscore a = if ((a>='A' && a<='Z') || (a>='a' && a<='z') || a=='_') 
     then True 
     else False 

ischarscoredigit :: Char -> Bool 
ischarscoredigit a = if ((a>='A' && a<='Z') || 
         (a>='a' && a<='z') || 
         a=='_' || 
         a>='0' && a<='9') then True else False 

isvar :: String -> Bool 
isvar [] = False 
isvar (h:t) = if (ischarorscore h) then (isvarbody t) else False 

isvarbody :: String -> Bool 
isvarbody [] = True 
isvarbody (h:t) = if (ischarscoredigit h) then (isvarbody t) else False 

isoperator :: Char -> Bool 
isoperator a = if (a=='+' || a=='-' || a=='*' || a=='/' || a=='^') then True else False 

issqrt :: String -> Bool 
issqrt [] = True 
issqrt x = if (x=="sqrt(") then True else False 

isnumber :: String -> Bool 
isnumber (h:t) = if (h>='0' && h<='9') then isnumber t else False 

charsetall :: String -> Bool 
charsetall [] = True 
charsetall (h:t) = if (h>='0' && h<='9' || 
       h>='a' && h<='z' || 
       h>='A' && h<='Z' || 
       h>='0' && h<='9' || 
       h=='+' || h=='-' || h=='*' || h=='/' || h=='^' ||   h=='(' || h==')' || h=='=') 
       then charsetall t else False 

charsetexpr :: String -> Bool 
charsetexpr [] = True 
charsetexpr (h:t) = if (h>='0' && h<='9' || 
       h>='a' && h<='z' || 
       h>='A' && h<='Z' || 
       h>='0' && h<='9' || 
       h=='+' || h=='-' || h=='*' || h=='/' || h=='^' || h=='(' || h==')') 
       then charsetexpr t else False 

paranthesis :: String -> Int -> Bool 
paranthesis [] a = if (a==0) then True else False 
paranthesis (h:t) a = if (h=='(') then (paranthesis t (a+1)) 
        else (if (h==')') then 
         paranthesis t (a-1) else paranthesis t a) 

paranthesis' :: String -> Bool 
paranthesis' (h:t) = paranthesis (h:t) 0 

obeyrules :: String -> Bool 
obeyrules [] = True 
obeyrules (h:t) = if (ischarorscore h) then (contvar t) 
       else if (h>='0' && h<='9') then (contnumber t) 
       else if (h=='(' || h=='-') then obeyrules t 
       else False 

contnumber :: String -> Bool 
contnumber [] = True 
contnumber (h:t) = if (h>='0' && h<='9') then contnumber t 
      else if (isoperator h) then contoperator t 
      else if (h==')') then contoperator h 
      else False 

contvar :: String -> Bool 
contvar [] = True 
contvar (h:t) = if (ischarorscore h || h>='0' && h<='9') then contvar t 
      else if (isoperator h) then contoperator t 
      else if (h==')') then contoperator 
      else False 

contoperator :: String -> Bool 
contoperator [] = True 
contoperator (h:t) = if (ischarorscore h) then contvar t 
       else if (h>='0' && h<='9') then contnumber t 
       else if (h=='(') then obeyrules t 
       else False 


isexpression :: String -> Bool 
isexpression [] = True 
isexpression (h:t) = if (charsetexpr (h:t) && paranthesis' (h:t) && obeyrules (h:t)) then True else False 

isexpression函数结合了上述所有功能来验证文件的一行。该项目是这样的:你必须阅读一个随机文件,看起来像上面那样,并包含变量名称=表达式和识别(,),+, - ,*,/,^这是功率,sqrt(k)其中k是数字或变量名称,mod,计算表达式并提供结果变量名称及其值。

+3

不明确的问题。你能不能改写你完成了什么(代码,也许?)以及你想达到的目标(例如输入和输出对)? –

+1

@larsmans'ghci'叫:)(告诉你关于['系统。Eval.Haskell'](http://hackage.haskell.org/package/plugins-1.5.1.4/docs/System-Eval-Haskell.html)) –

+0

你可能正在寻找一名口译员,目前还不清楚。考虑使用提示,例如:http://stackoverflow.com/questions/5582926/haskell-how-to-evaluate-a-string-like-12/5584638#5584638 –

回答

5

你很可能想要解析所谓的抽象语法树。对于这里给出的片段,它可能看起来像

data AST 
    = Var String 
    | Lit Double 
    | Plus AST AST 
    | Mult AST AST 
    | Sqrt AST 
    | Assign String AST 
    | Then AST AST 

有了这个选择的AST,代码片段你给的模样

fragment :: AST 
fragment = foldr1 Then [ 
    Assign "x" (Lit 3) 
    , Assign "y" (Lit 4) 
    , Assign "z" (Plus (Var "x") (Var "y")) 
    , Assign "badluck" (Plus (Var "x") (Mult (Sqrt (Var "z")) (Lit 7))) 
    ] 

然后我们挑选其中将这些AST值的评估函数到他们的“典型”价值。在这种情况下,基本的规范值应该是Double,但由于AST可能因参考未知变量而失败,我们将使用规范值Maybe Double代替。最后,由于我们预计环境会随着表达式的评估而发生变化,我们会返回更新后的环境。

为了操作这个评估器,我们需要一个被分配到的环境的概念。这方面的一个很好的选择是Data.Map

import   Control.Applicative 
import qualified Data.Map   as Map 
import   Data.Map   (Map) 

type Env = Map String Double 

eval :: Env -> AST -> Maybe (Double, Env) 
eval env (Var s) = fmap (\x -> (x, env)) (Map.lookup s env) 
eval env (Plus e1 e2) = do -- maybe monad 
    (v1, env') <- eval env e1 
    (v2, env'') <- eval env' e2 
    return (v1 + v2, env'') 
eval env (Mult e1 e2) = do -- maybe monad 
    (v1, env') <- eval env e1 
    (v2, env'') <- eval env' e2 
    return (v1 * v2, env'') 
eval env (Sqrt e)  = fmap (\(x, e) -> (sqrt x, e)) (eval env e) 
eval env (Assign s e) = do 
    (v, env') <- eval env e 
    return (v, Map.insert s v env') 
eval env (Then e1 e2) = do 
    (_, env') <- eval env e1 
    (v, env'') <- eval env' e2 
    return (v, env'') 

从技术上讲,这可能已经使用的State EnvMaybe单子变压器栈完成,但如果编写繁琐的操作可能是一个更加清楚一点。

+0

这是伟大的,但我不太了解代码。我已经阅读过Parsec文档和Real World Haskell第16章,但仍然无法将其包围。这个代码不是Parsec,但仍然试图理解伤害。此外,我更新了问题 – user3665704

+0

Parsec是一种构建像'parse :: String - > AST'的函数的方法。这是第一步。然后你用'eval Map.empty :: AST - > Maybe(Double,Env)''来评估AST。 –

2

你为什么不使用Data.Map? Haskell中没有“变量”,所以它可以更好地为您服务,并且使用起来会更容易。


要详细一点:一旦你从文件中读取一个变量标识符与所需intialization语法,就像这样:

<<name>> = <<value>> 

插入一个新的进入你的Map,这将只是一个(name, value)对。名称显然是String类型的,并且浮点类型应该是正确的值类型(如果您只处理数字)。每次变量赋值都依赖于另一个变量,只需要lookup它并使用该值。要保存不断变化的值图,可以简单地使用State

+0

很简单的解释,但好主意仍然 – user3665704