2011-12-12 189 views
7

我試圖爲給定的布爾表達式生成一個真值表。我可以通過創建一個新的數據類型BoolExpr來做到這一點,但我想用一個匿名函數來做到這一點。它應該是這樣工作的:Haskell中匿名函數的真值表

> tTable (\x y -> not (x || y)) 
output: 
F F | T 
F T | F 
T F | F 
T T | F 

我的方法:

tbl p = [(uncurry p) tuple | tuple <- allval] 
     where allval=[(x,y) | x <- [False,True], y <- [False,True]] 

這工作,但只有2個參數。我想爲任何數量的參數做它。所以,我想我會做一個函數,從列表中取參數:

argsFromList f []  = f 
argsFromList f (x:xs) = argsFromList (f x) xs 

這不起作用:

Occurs check: cannot construct the infinite type: t = t1 -> t 
    Expected type: t -> [t1] -> t1 -> t 
    Inferred type: (t1 -> t) -> [t1] -> t1 -> t 
In the expression: argsFromList (f x) xs 

我不明白的問題是在這裏。 如果有人能指出我正確的方向或發佈鏈接,我將不勝感激。

+0

可能的重複[爲什麼在Haskell中不允許這樣的函數定義?](http://stackoverflow.com/questions/6168880/why-is-such-a-function-definition-not-allowed-in- haskell) –

+0

請注意,您可以將lambda定義爲'(\ [x,y] - > not(x || y))',這會自動爲您提供「具有任意多個參數的函數的所有相同類型的行爲」。 – leftaroundabout

回答

4

這裏的問題是,你試圖遞歸調用遞歸步驟的不同類型的函數。考慮定義:

argsFromList f []  = f 
argsFromList f (x:xs) = argsFromList (f x) xs 

讓我們嘗試自己推斷類型。我們可以立即看到第一個參數f應該是至少一個參數的函數,第二個參數(x:xs)是一個列表,並且列表元素應該與第一個參數f的類型相同。在第一種情況下,返回參數f,所以最終返回類型必須與第一個參數相同。因此,我們開始與此:

argsFromList :: (a -> ?) -> [a] -> (a -> ?) 

要找到未知類型?,大家可以看一下第二種情況下,它由一個遞歸調用。參數xs是相同的列表類型,參數(f x)的類型爲?。由於它被用作遞歸調用中的第一個參數,其類型爲(a -> ?),所以我們現在可以得出結論:?(a -> ?)的類型相同,因此它與(a -> (a -> ?))的類型相同,因此它與(a -> (a -> (a -> ?)))的類型相同。 。oops。

當然,這將是「無限型」。

如果您希望使用可變數量的單一類型參數的函數來完成此操作,您可能需要使用帶有值列表而非單獨參數的函數。否則,你將不得不單獨編寫每個版本,或者使用一些涉及高級語言功能的奧祕技巧,這兩種方法在這樣的簡單情況下都不具吸引力。

+0

謝謝!我會放過它,然後使用新的數據類型。 – 1nterference

13

如果你想建立的布爾函數的真值表與參數任意數量,您正在創建必須爲多種類型的工作的功能,所以你必須使用類型類:

{-# LANGUAGE FlexibleInstances #-} 

class TruthTable a where 
    truthTable :: a -> [([Bool], Bool)] 

instance TruthTable Bool where 
    truthTable b = [([], b)] 

instance TruthTable a => TruthTable (Bool -> a) where 
    truthTable f = [ (True : inps, out) | (inps, out) <- truthTable (f True)] ++ 
       [ (False : inps, out) | (inps, out) <- truthTable (f False)] 

例如:

*Main> mapM_ print $ truthTable (&&) 
([True,True],True) 
([True,False],False) 
([False,True],False) 
([False,False],False) 
2

什麼你問是不是所有瑣碎。 Haskell並不容易處理使用可變數量參數的函數。例如,對於不同數量的參數(zip,zip3zip4,...),zip functions from Data.List有單獨的變體。同樣,在Control.MonadliftMliftM2liftM3 ...

基本上,你可以分配給功能與參數數目未知的最普遍的類型是a -> b;一地方真相功能是Bool -> Bool(A = Bool,B = Bool),雙地方真理功能是Bool -> (Bool -> Bool)(A = Bool,B = Bool -> Bool),三地方是Bool -> (Bool -> (Bool -> Bool))(A = Bool,B = Bool -> (Bool -> Bool)) , 等等。但是沒有簡單的方法可以查看已傳入的函數,以瞭解初始箭頭右側的類型。

一種可以工作的解決方案涉及使用類型類爲每個參數函數類型定義真值表製造函數的單獨實例。 Sjoerd Visscher在此線程中的回答是通過使用遞歸實例定義(注意遞歸TruthTable a => TruthTable (Bool -> a)聲明)爲所有函數大小執行此操作。可能有其他解決方案可以使用Applicative類型類構建。