2014-05-22 87 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