我正在使用抽象整数计算器,我正在进行大量的模式匹配。我可以写
add Zero x = x
add (P x) y = next $ add (prev $ P x) y
add (N x) y = prev $ add (next $ N x) y
或
add Zero x = x
add x y = case x of
P _ -> next $ add (prev x) y
_ -> prev $ add (next x) y
虽然第一种方式更短,但第二种方式更吸引我。
这是首选方法吗?
答案 0 :(得分:3)
使用as-patterns。
add Zero y = y
add x@(P _) y = next $ add (prev x) y
add x@(N _) y = prev $ add (next x) y
我还考虑通过注意你只是交换prev
和next
函数的角色来抽象出两个递归分支的公共结构,这取决于x
是积极的还是消极的:
add Zero x = x
add x y = f $ add (g x) y
where (f, g) = case x of
P _ -> (next, prev)
N _ -> (prev, next)
答案 1 :(得分:1)
关于这种风格:
add Zero x = x
add x y = case x of
P _ -> next $ add (prev x) y
_ -> prev $ add (next x) y
从积极的方面来说,它避免了一些重复,这很好。
从消极方面看,case
看起来并非详尽无遗。实际上,为了说服自己模式匹配真的很详尽,我们必须推断x
中case x of
的可能值,并在运行时看到它不能是Zero
,因为这是上面处理的。这需要比第一个片段更多的心理努力,第一个片段显然详尽无遗。
更糟糕的是,当我们应该像往常一样打开警告时,GHC会抱怨,因为它不相信case
是详尽无遗的。
就个人而言,我希望Haskell的设计师完全禁止非详尽的比赛。如果有的话,我会使用-Werror-on-non-exhaustive-matches
。我想被迫写下来。
case something of
A -> ...
B -> ...
_ -> error "the impossible happened"
而不是编译器为我默默插入最后一个分支。
答案 2 :(得分:1)
考虑在等价关系下使用math-style definition of integers作为自然对的同余类:
T
直觉是这对自然{((a,b), (c,d)) | b+c == d+a}
代表(a,b)
。正如维基百科文章中所提到的,与“0 /肯定/否定”定义相比,这通常会减少特殊情况的数量。特别是,您要求实施的添加操作变成了一个单行:
b-a
通过这种表示来完成不同的操作是很有趣的。例如,-- both Int and Integer are taken
data Int' = Int Nat Nat
instance Num Int' where
-- b-a + d-c = (b+d)-(a+c)
Int a b + Int c d = Int (a + c) (b + d)
可以使用上面给出的等式实现,Eq
类似:
Ord
有时,将这些事情规范化可能很方便。就像分数可以通过将分子和分母乘以相同的数字来减少直到它们是相对素数一样,这些东西可以通过向两个部分加上或减去相同的数字来减少,直到(至少)其中一个为零。 / p>
instance Eq Int' where
-- b-a == d-c = b+c == d+a
Int a b == Int c d = b+c == d+a
instance Ord Int' where
-- compare (b-a) (d-c) = compare (b+c) (d+a)
compare (Int a b) (Int c d) = compare (b+c) (d+a)