合并排序中使用的merge
函数可以定义为:
merge a@(x:xs) b@(y:ys) | x < y = x : merge xs b
| otherwise = y : merge a ys
merge [] b = b
merge a [] = a
该函数只是通过获取相对较小的测试条件为“更真实”的列表的头部来生成结果,其中对于空列表的不存在的头部是假的,并且两个列表都是只走过一次。因此,应该可以使用一般的迭代函数并将x < y
谓词给出的两个执行路径包含在一个中,这样函数读取的内容就像我的第一句话中的英文描述一样。我该怎么做?
尽管merge
函数已经完全可读,但我有时会发现难以编写的双递归函数,所以我很乐意学习更多的方法。
答案 0 :(得分:3)
您可以将模式匹配分成两部分:
merge (a:as) bs = loop bs where
loop (b:bs) | b < a = b : loop bs
loop bs = a : merge as bs
merge [] bs = bs
也可以使用span
merge (a:as) bs = lts ++ a : merge as ges
where (lts, ges) = span (<a) bs
merge [] bs = bs
答案 1 :(得分:2)
根据测试结果,您可以使用不同的参数进行单个递归调用。
$json_response
PS,我不会称你的merge xxs@(x:xs) yys@(y:ys) =
let (z, xs', ys') = if x <= y then (x, xs, yys) else (y, xxs, ys)
in z : merge xs' ys'
merge [] ys = ys
merge xs [] = xs
加倍递归。每个代码路径最多只有一个递归调用。
答案 2 :(得分:2)
老实说,我觉得你的英文描述很难理解。 '对于哪个测试条件......是“更真实”' - 什么?但我会尝试,因为我喜欢玩弄短语。首先,我们需要一种方法来表达“空列表中不存在的头部的错误”。对我而言,我们需要一种类似Maybe
的数据类型,但“无”的情况是“无限大”。以下是:
data AdjInf a = Finite a | Infinite
deriving (Eq, Ord)
(就像Maybe
一样,但构造函数顺序颠倒过来 - Ord
派生会处理其余部分!)
我们可以从这个方面获得列表的头部:
head' :: [a] -> AdjInf a
head' [] = Infinite
head' (x:xs) = Finite x
现在我们有:
merge :: (Ord a) => [a] -> [a] -> [a]
merge [] [] = []
merge xs ys = next : merge smaller bigger
where
(next : smaller, bigger)
| head' xs <= head' ys = (xs, ys)
| otherwise = (ys, xs)
哪些可能符合您的标准。 (此递归模式是一个列表 anamorphism ,因此您可以使用unfoldr
编写它<)
我会避免这种特殊的实现,因为next : smaller
模式匹配总是成功的事实是非常微妙的。如果列表包含比较相等的不同元素(即,merge
不是“稳定”),它也不是完全相同的函数。)