比较Haskell中的函数

时间:2010-12-01 20:11:36

标签: haskell

有没有办法比较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]

5 个答案:

答案 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那样),但这通常是不明智的,因为如果你不小心它会导致组合爆炸。

有多种方法可以实现您的直接目标,例如:

  • 不要直接使用succpred,而是使用类型为Enum a => a -> Maybe a的自我保护函数来生成Nothing而不是例外。

  • 传入“停止”值以检查,例如search op end x list = if x == end then [] ....

  • 完全忘记succpred,只需传入要比较的值列表,例如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 []