编写双重递归函数而不进行模式匹配

时间:2018-05-24 03:51:53

标签: haskell recursion

合并排序中使用的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函数已经完全可读,但我有时会发现难以编写的双递归函数,所以我很乐意学习更多的方法。

3 个答案:

答案 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不是“稳定”),它也不是完全相同的函数。)