有没有办法比较Haskell中的两个函数?
我的想法是答案是否定的,因为函数不会派生Eq类型。但是我正在尝试编写一个非常简单的函数,这似乎是一件正常的事情:
search :: ((Enum a) => a -> a) -> Card -> [Card]
search op x list = if (op == succ && rank x == King) ||
(op == pred && rank x == Ace)
then []
else let c = [ n | n <- list, rank n == op (rank x)]
in if length c == 1
then x : search op (head c) list
else []
错误讯息:
No instance for (Eq (Rank -> Rank))
arising from a use of `=='
基本上,它可以向上或向下搜索卡片列表,以查找与x中的下一个或上一个排名卡片匹配的卡片,从而构建列表。通过将'pred'或'succ'函数作为运算符,它可以向前和向后工作。但是,我需要检查它是否不会超出枚举范围,否则会引发异常。
所以我正在寻找一种方法来防止异常或解决这个问题!
关于改进代码的任何其他指示也将受到赞赏:)
感谢所有精彩提示,这是我提出的解决方案(真正从每个答案中获取一些内容!):
编辑:下面的正确解决方案:
maybeSucc x | x == maxBound = Nothing
| otherwise = Just (succ x)
maybePred x | x == minBound = Nothing
| otherwise = Just (pred x)
-- takes a list of cards which have a rank one op than x
-- only if there is exactly one is it sequential, otherwise end matching
search :: (Rank -> Maybe Rank) -> Rank -> [Card] -> [Card]
search op x list = case filter (\n -> Just (rank n) == op x) list of
[y] -> y : search op (rank y) list
_ -> []
测试:
*Main> let cards = [Card Ace Heart, Card Two Club, Card Three Spade, Card Five Club, Card Four Diamond]
*Main> search maybeSucc Two cards
[Three of Spades,Four of Diamonds,Five of Clubs]
*Main> search maybePred Three cards
[Two of Clubs,Ace of Hearts]
答案 0 :(得分:23)
没有,也绝不会是比较两个函数是否相等的方法。有一个数学证明一般不可能。
“实用”方法将取决于编译器的内部工作方式(例如,如果两个函数在内部由相同的内存地址表示,则它们是相等的),或者没有预期的那么有用。 succ ==(+1)的预期结果是什么?如果在succ = =(+ a)中让a == 1?
我想不出一种方法来定义(==)总是有意义的函数,我相信其他任何人都不能。
类型签名缺少返回类型。
您的函数具有rank 2类型,这不是标准的Haskell 98,更重要的是,在这种情况下不是必需的。你不关心传入的函数是否可以处理任何枚举类型,你只关心它适用于Rank:
search :: (Rank -> Rank) -> Card -> [Card] -> [Card] -- much simpler.
我会尝试更频繁地使用案例,如果/那么不经常使用案例。特别是,我写道:
case [ n | n <- list, rank n == op (rank x)] of
[y] -> x : search op y list -- length == 1
_ -> [] -- otherwise
你的函数基本上只对op有两个不同的值,但它的类型并没有这么说;这对我来说不合适。
你可以用老式的方式做到:
data Direction = Succ | Pred deriving(Eq)
search :: Direction -> Card -> [Card] -> [Card]
search dir x list = if (dir == Succ && rank x == King) ...
... let op = case Direction of Succ -> succ ; Pred -> pred
in ...
或者你可以使参数更通用,并传递一个可能正常失败的函数(通过返回Nothing):
maybeSucc x | x == maxBound = Nothing
| otherwise = Just (succ x)
search :: (Rank -> Maybe Rank) -> Card -> [Card] -> [Card]
search op x list = case op (rank x) of
Nothing -> []
Just theRank -> case [ n | n <- list, rank n == theRank ] of ...
答案 1 :(得分:7)
1)你的op
过于笼统。您只会将卡用于rank (undefined :: Card)
类型,因此只需将其设为RankThing -> RankThing
即可。此外,您的函数类型签名缺少返回值类型。
2)一个预期用途的示例看起来像是search succ Ace xs
,但这是相当笨拙的,处理边界的两个辅助函数怎么样:
searchUp King _ = []
searchUp x ys = search succ x ys
searchDown Ace _ = []
searchDown x ys = search pred x ys
这可能会为您的用户更好地阅读,并且无需检查操作
3)如果你真的想检查执行了什么操作,并且知道操作将是两种可能之一,那么你可以命名操作或用已知的答案测试(KAT)测试它。例如:
data Operation = Succ | Pred
search :: Operation -> Card -> [Card] -> []
search Succ King _ = []
search Succ x ys = search' succ x ys
search Pred Ace _ = []
search Pred x ys = search' pred x ys
KAT解决方案(相当蹩脚,imo):
search op x ys
| op Five == Four && rank x == Ace = []
| op Five == Six && rank x == King = []
| otherwise = ...
答案 2 :(得分:7)
嗯,在Haskell中没有“引用相等”的概念,并且在一般情况下无法检查函数的“行为相等”。虽然可以对仅在小型有限类型上运行的函数进行操作(如Rank
那样),但这通常是不明智的,因为如果你不小心它会导致组合爆炸。
有多种方法可以实现您的直接目标,例如:
不要直接使用succ
和pred
,而是使用类型为Enum a => a -> Maybe a
的自我保护函数来生成Nothing
而不是例外。
传入“停止”值以检查,例如search op end x list = if x == end then [] ....
完全忘记succ
和pred
,只需传入要比较的值列表,例如search :: [Rank] -> Card -> [Card] -> [Card]
,如果排名列表为空,则结束搜索。
关于您的代码的其他一些评论:
你的列表理解正在重新发挥作用 - 寻找能够做你想做的标准库函数,例如filter
。
请勿将列表的length
与较小的常量 - 使用模式匹配进行比较。
答案 3 :(得分:5)
只要函数在适当的域和范围(Enum,Bounded,Eq等,域和范围略有不同的组合)上,您就可以比较有限函数,甚至更高 - 以直接的方式订购,然后使用类型类自动化流程。
我在一篇Haskell邮件列表的帖子中写了这个想法,然后(更恰当地)在我在其中一个FDPE会议(维多利亚,BC)上发表的paper中。我之前从未见过它,但是如果其他人也做过这件事,我也不会感到惊讶:这是一个非常简单的想法,一旦你克服了这个问题&#34;功能可以#&#34; 39;为了平等而进行比较&#34;。当然,关于偏袒和严格性的常见警告适用:例如,你不能为两个函数返回True,这两个函数都不能因某些常见参数而终止。
但是对于有限域,这种技术起作用并提供了许多实际用途。 (加上它真的很有趣:))。
编辑:我将代码添加到hpaste here;它适用于Hugs,但需要对GHC进行一些调整。
编辑2:我添加了一个pragma并对hpaste-d代码进行了注释,以便它可以在GHC和Hugs中运行。 我还将此示例添加为test3(它按预期返回True,并按短时间顺序):
(\x -> [(&&x), (||x), not]) == (\y -> [(y&&), (y||), not . not . not])
答案 4 :(得分:1)
下面的代码可能无法编译,但希望你能得到这个想法:
data Op = Succ | Pred deriving Eq
fromOp :: Enum a => Op -> a -> a
fromOp Succ = succ
fromOp Pred = pred
search :: Op -> Card -> [Card] -> [Card]
search op x list = if (op == Succ && rank x == King) ||
(op == Pred && rank x == Ace)
then []
else let c = [ n | n <- list, rank n == (fromOp op) (rank x)]
in if length c == 1
then x : search op (head c) list
else []