2017-10-09 162 views
5

如果我有三種數據類型,分別爲ExprOpFu將自定義數據類型轉換爲字符串

data Expr = Num Double 
      | X 
      | Operator Op Expr Expr 
      | Function Fu Expr 
      deriving (Eq) 

data Op = Add | Mul 
     deriving (Eq) 

data Fu = Sin | Cos 
     deriving (Eq) 

我應該做的是後來創建一個基於數據類型的表達式。例如

let anExpr = Add (Num 3.0) (Num 4.0) 

而且打印出相應的表達式,在這種情況下,它應該只是「3.0 + 4.0」。

我面臨的問題是創建另一種類型或數據類型來識別它是否應該打印出來的加法或乘法符號。我想要做的就是這樣的僞代碼

printOut (Num n) = show n 
printOut X = "x" 
printOut (Op a b) = printOut a ++ 'printOutRightOp' ++ printOut b 
printOut (Fu a) = 'printOutRightFu' ++ printOut a 

我該如何做到這一點? 。

+0

但是'Op'和'Fu'在'Expr'是建設者的** **的名字。 –

+0

對不起,修正了這個問題 – Salviati

+0

@Salviati你剛開始有一個拼寫錯誤,應該是「如果我有三種數據類型** Expr **,Op和Fu」...... – jkeuhlen

回答

5

不能寫入類型的引用作爲構造名。在您的代碼:

data Expr = Num Double 
      | X 
      | Op Expr Expr 
      | Fu Expr 
      deriving (Eq)

OpFu是構造的名。 Haskell認爲這是一個同名的類型,這只是一個巧合。您必須添加操作符/函數的類型作爲第一個參數。所以:

data Expr = Num Double 
      | X 
      | Op Op Expr Expr 
      | Fu Fu Expr 
      deriving (Eq)

現在第二OpFu指定類型的第一個參數。

現在,所以你必須更換:

let anExpr = Add (Num 3.0) (Num 4.0) 

有:

let anExpr = Op Add (Num 3.0) (Num 4.0) 

此基礎上,我們可以定義一些幫手功能:

showOp :: Op -> String 
showOp Add = "+" 
showOp Mul = "*" 

showFu :: Fu -> String 
showFu Sin = "sin" 
showFu Cos = "cos" 

現在我們當我們定義我們的時可以使用這些幫助函數(我不會叫它printOut,因爲功能確實不是而是打印的東西,它只會產生一個字符串,你可以對該字符串做任何你想做的事情)。

showExpr :: Expr -> String 
showExpr (Num n) = show n 
showExpr X = "x" 
showExpr (Op o a b) = '(' : showExpr a ++ ')' : showOp o ++ '(' : showExpr b ++ ")" 
showExpr (Fu f a) = showFu f ++ '(' : showExpr a ++ ")" 

例如:

*Main> showExpr (Op Add (Num 3.0) (Num 4.0)) 
"(3.0)+(4.0)" 
*Main> showExpr (Op Add (Num 3.0) (Fu Sin (Num 4.0))) 
"(3.0)+(sin(4.0))" 
3

當你的模式匹配,你需要匹配你傳遞的構造在您的例子,一個Expr可以有一個Operator構造包含相關信息打印:

printOut (Num n) = show n 
printOut X = "x" 
printOut (Operator op a b) = printOut a ++ printOp op ++ printOut b 
printOut (Function fu a) = printFun fu ++ printOut a 

現在你只定義附加圖案爲您的操作符和函數匹配:

printOp Add = "+" 
printOp Mul = "x" 

printFun Sin = "Sin" 
printFun Cos = "Cos" 

除了什麼@WillemVanOnsem寫的,你居然不打印任何東西到屏幕上與這些「打印」功能。相反,你正在將它們從你的數據類型轉換成一個字符串。有一種常見的類型類別叫做Show。你也可以只創建節目的情況下,爲你的類型,並使用該功能,而不是顯示它們:

instance Show Fu where 
    show Sin = "Sin" 
    show Cos = "Cos" 

然後用show代替:

printOut (Function fu a) = show fu ++ printOut a 

當你擁有的show一個簡單的實例,您也可以使用與您使用衍生的相同語法爲您自動導出它:Eq

data Fu = Sin | Cos 
     deriving (Show, Eq) 

爲了應對創造Expr一個實例,再使用各種構造這樣做:

let anExpr = Operator Add (Num 3.0) (Num 4.0) 
+0

Ty太多了,真的感覺數據類型是現在更清晰了。在被理解之前,這真的是一個痛苦的屁股。 – Salviati

+2

顯示實例通常應該打印有效的Haskell代碼,可以通過合理的Read實例將其讀回爲相同的值。當你想以不同的格式打印數據時,就像這裏一樣,最好爲它定義一個單獨的函數,而不是重新調整Show。 – amalloy

+0

@amalloy好點。你仍然可以創建一個自定義的'read'實例來滿足'read'。 show == id',但在這種情況下,你可能不希望操作符。也許'Fu'型在這裏是一個更好的例子。 – jkeuhlen

相關問題