Haskell中匿名函数的真值表
Posted
技术标签:
【中文标题】Haskell中匿名函数的真值表【英文标题】:Truth Tables from Anonymous Functions in Haskell 【发布时间】:2012-01-18 16:02:30 【问题描述】:我正在尝试为给定的布尔表达式生成一个真值表。我可以通过创建一个新的数据类型 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 个参数。我想为任意数量的参数做这件事。所以我想我会做一个从列表中获取参数的函数:
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
我不明白这里有什么问题。 如果有人能指出我正确的方向或发布链接,我将不胜感激。
【问题讨论】:
Why is such a function definition not allowed in haskell?的可能重复 请注意,您可以将 lambda 定义为(\[x,y] -> not (x || y))
,这会自动为您提供“具有任意多个相同类型参数的函数”的行为。
【参考方案1】:
如果您想为具有任意数量参数的布尔函数构建真值表,您将创建一个必须适用于多种类型的函数,因此您必须使用类型类:
-# 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】:这里的问题是,您正在尝试使用不同类型的递归调用递归步骤的函数。考虑定义:
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 -> ?)))
相同的类型是...哎呀。
那当然是“无限类型”。
如果您想对使用可变数量的单一类型参数的函数执行此操作,您可能希望使用接受值列表而不是单个参数的函数。否则,您将不得不单独编写每个版本,或者使用一些涉及高级语言功能的神秘技巧,在这种简单的情况下,这两种方法都没有吸引力。
【讨论】:
谢谢!那就顺其自然吧,然后使用新的数据类型。【参考方案3】:您所要求的并非微不足道。 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
类型类构建。
【讨论】:
以上是关于Haskell中匿名函数的真值表的主要内容,如果未能解决你的问题,请参考以下文章