我正在尝试为给定的布尔表达式生成真值表。我可以通过创建一个新的数据类型BoolExpr来做到这一点,但我想用匿名函数来做。它应该像这样工作:
> 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个参数。我想为任意数量的参数做这件事。所以我想我会创建一个从List中获取Arguments的函数:
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 :(得分: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)
答案 1 :(得分:4)
这里的问题是你试图以递归步骤的不同类型递归调用函数。考虑定义:
argsFromList f [] = f
argsFromList f (x:xs) = argsFromList (f x) xs
让我们尝试自己推断出这种类型。我们可以立即看到第一个参数f
应该是至少一个参数的函数,第二个参数(x:xs)
是一个列表,列表元素应该与{的第一个参数的类型相同。 {1}}。在第一种情况下,返回参数f
,因此最终返回类型必须与第一个参数相同。所以我们从这开始:
f
要查找未知类型argsFromList :: (a -> ?) -> [a] -> (a -> ?)
,我们可以查看第二种情况,它包含递归调用。参数?
与列表类型相同,参数xs
的类型为(f x)
。由于它被用作递归调用中的第一个参数,类型为?
,我们现在可以得出结论:(a -> ?)
与?
的类型相同,因此类型与(a -> ?)
相同{1}}因此与(a -> (a -> ?))
的类型相同,即... oops。
当然,这将是“无限型”。
如果要对使用单个类型的可变数量的参数的函数执行此操作,您可能希望使用带有值列表而不是单个参数的函数。否则,您必须单独编写每个版本或使用一些涉及高级语言功能的神秘技巧,在这种简单的情况下,这些技巧都不具吸引力。
答案 2 :(得分:2)
你所要求的并不是微不足道的。 Haskell并不容易处理应用具有可变数量参数的函数的函数。例如,zip functions from Data.List
针对不同数量的参数(zip
,zip3
,zip4
,...)提供了单独的变体。同样,在Control.Monad
liftM
,liftM2
,liftM3
,...
基本上,您可以为具有未知数量参数的函数分配的最常规类型是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
类型类构建。