2015-11-07 76 views
1

我有一個學校項目,我需要簡化用戶生成的數學表達式。我們還需要定義我們自己的基本操作符(:+:是加法,:*:是乘法等)。實現自定義運算符

例如:

Prelude> Const 9 :+: Const 3 :+: Const 2 
Const 14 
Prelude> Const 14 :*: Var 'x' :+: Const 10 :*: Var 'x'  
Const 24 :*: Var 'x' 

這是我到目前爲止有:

infixl 4 :+: 
infixl 5 :*:, :/: 
infixr 6 :^: 

data Expr a = Var Char 
     | Const a 
     | (Expr a) :+: (Expr a) 
     | (Expr a) :*: (Expr a) 
     | (Expr a) :^: (Expr a) 
     | (Expr a) :/: (Expr a) 
     deriving (Show, Eq) 

我已經試過這樣的事情對我的加法操作沒有成功:

class (Num a) => Expr a where 
    (:+:) :: a -> a -> a 

instance Expr a where 
    x :+: y = x + y 

所有我正在尋找的是一些關於如何開始這項工作的指導。我很喜歡創建自己的功能,並使用常用功能(mapzipfoldr等)。我們最近開始學習類型類,函子,以及關於單子的簡介。

+0

'x:+:y'中的'x'和'y'是'Expr a''。你將不得不實際執行整個簡化的事情,例如'Const a:+:Const b = Const(a + b)' – Ryan

+1

您可能想從一個函數'evaluate :: Fractional a => Expr a - > a'開始。 (你將需要'Fractional'來劃分,其他的都在'Num'中。) – 2015-11-07 21:03:59

+0

你可能必須使用模式匹配'simp(x:*:(y:/:x'))來實現簡化。 x == x'= y;簡化...「完全」簡化這個表達式是相當不重要的,所以我不知道你期望執行哪些簡化。 – user2407038

回答

3

你在混淆兩個非常不同的主題(如果你粘貼了錯誤信息,這可能會更早出現並讓你獲得更好的反饋)。讓我們看看提供給你的是什麼 - Expr數據類型和類。

一個優雅的混亂

你都具備:

data Expr a = Var Char 
     | Const a 
     | (Expr a) :+: (Expr a) 
     | (Expr a) :*: (Expr a) 
     | (Expr a) :^: (Expr a) 
     | (Expr a) :/: (Expr a) 
     deriving (Show, Eq) 

在這裏我們可以看到構造函數是基本的算術運算。換言之,:+:Expr a -> Expr a -> Expr a類型的構造函數,其他類似。

現在你去嘗試,並定義一個函數,即:+:

class (Num a) => Expr a where 
    (:+:) :: a -> a -> a 

instance Expr a where 
    x :+: y = x + y 

這裏有幾個問題。

  • 您的Expr只是與上述類型命名相同,它與上面定義的數據無關。
  • 您的方法:+:不是合法的函數名稱。與數據構造函數不同,函數必須是有效變量,因此不能以大寫或冒號字符開頭。
  • 該實例適用於所有類型 - a是一個變量,可以匹配任何類型。所以你還沒有說xy值的類型是Expr a

我認爲你試圖做一個類每一個運營商,一個實例爲每個構造(:+:,:*:, etc). This is a broken, or at least overly verbose, solution. The only reason to use classes are if there are multiple type that require instances - you only have the one Expr`型

無類

沒有必要。可以使用傳統函數來評估表達式,就像在已刪除的答案中一樣

先前刪除的答案似乎被刪除,並且每個va的綁定環境riable是必要的。我對這個問題的閱讀是不同的,我只是簡單地將表達式評估爲一個簡單的字符串。

evaluate :: Show a => Expr a -> String 
evaluate (Var c) = [c] 
evaluate (a :+: b) = undefined 
evaluate (a :*: b) ... so on and so forth ... 

在上述你圖案它們以這樣的方式依賴於構造(+:+:等結合之前匹配您Expr a以確定操作然後,通過根據具體情況,evalute子表達式作爲必要)。

0

我能夠實施這個項目,我非常感謝幫助我開始正確的方向。如果有人感興趣,我會在這裏發佈我的解決方案。它可能不漂亮,但它的工作原理!

completeCleanUpcleanUp的幫助下簡化了Expr。 plugIn需要一些Char 'x',並用提供的數字替換所有Var 'x'。最後evalExpr給Expr返回一個解決方案。

請注意,變量簡化不需要執行,因此Const 9 :*: Var 'x' :+: Const 10 :*: Var 'x'不會進一步減少。

cleanUp :: (Eq a, Floating a) => Expr a -> Expr a 

cleanUp (Const a) = Const a 
cleanUp (Var a) = Var a 
cleanUp (Const a :*: (Const b :*: c)) = Const (a * b) :*: cleanUp c 
cleanUp (Const a :*: c :*: Const b) = Const (a*b) :*: cleanUp c 
cleanUp (c :*: Const a :*: Const b) = Const (a*b) :*: cleanUp c 
cleanUp (Const a :*: (b :+: c)) = (Const a :*: cleanUp b) :+: (Const a :*: cleanUp c) 
cleanUp (Const 0 :/: a) = Const 0 
cleanUp (a :/: Const 0) = error "Error: Division by 0 not allowed." 
cleanUp (a :/: b) | a == b = Const 1.0 
cleanUp (a :^: Const 1) = cleanUp a 
cleanUp (Const 1 :^: a) = cleanUp a 
cleanUp (a :^: Const 0) = Const 1.0 
cleanUp (Const 0 :^: a) = Const 1.0 
cleanUp ((c :^: Const b) :^: Const a) = cleanUp c :^: Const (a*b) 
cleanUp (Const a :^: Const b) = Const (a ** b) 
cleanUp (Const a :/: Const b) = Const (a/b) 
cleanUp (a :*: Const 1) = cleanUp a 
cleanUp (Const 1 :*: a) = cleanUp a 
cleanUp (a :*: Const 0) = Const 0 
cleanUp (Const 0 :*: a) = Const 0 
cleanUp (Const a :*: Const b) = Const (a * b) 
cleanUp (a :+: Const 0) = cleanUp a 
cleanUp (Const 0 :+: a) = cleanUp a 
cleanUp (Const a :+: Const b) = Const (a + b) 
cleanUp (Var a :^: b) = Var a :^: cleanUp b 
cleanUp (a :^: Var b) = cleanUp a :^: Var b 
cleanUp (Var a :+: b) = Var a :+: cleanUp b 
cleanUp (a :+: Var b) = cleanUp a :+: Var b 
cleanUp (Var a :*: b) = Var a :*: cleanUp b 
cleanUp (a :*: Var b) = cleanUp a :*: Var b 
cleanUp (Var a :/: b) = Var a :/: cleanUp b 
cleanUp (a :/: Var b) = cleanUp a :/: Var b 
cleanUp (a :^: b) = cleanUp a :^: cleanUp b 
cleanUp (a :/: b) = cleanUp a :/: cleanUp b 
cleanUp (a :*: b) = cleanUp a :*: cleanUp b 
cleanUp (a :+: b) = cleanUp a :+: cleanUp b 

completeCleanUp :: (Eq a, Floating a) => Expr a -> Expr a 

completeCleanUp exp 
    | exp == cleanUp exp = exp 
    | otherwise = completeCleanUp (cleanUp exp) 


plugIn :: Char -> a -> Expr a -> Expr a 

plugIn char num (Var c) | char == c = Const num 
plugIn char num (a :^: b) = plugIn char num a :^: plugIn char num b 
plugIn char num (a :/: b) = plugIn char num a :/: plugIn char num b 
plugIn char num (a :*: b) = plugIn char num a :*: plugIn char num b 
plugIn char num (a :+: b) = plugIn char num a :+: plugIn char num b 
plugIn char num a = a 


evalExpr :: (Eq a, Floating a) => Char -> a -> Expr a -> a 

evalExpr char num exp = case (completeCleanUp . plugIn char num $ exp) of 
    (Const x) -> x 
    _ -> error "Cannot further simplify solution."