Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
List 尾部递归排序和合并两个列表的方法_List_Haskell - Fatal编程技术网

List 尾部递归排序和合并两个列表的方法

List 尾部递归排序和合并两个列表的方法,list,haskell,List,Haskell,我正在尝试编写一种尾部递归方法,将两个排序列表合并为一个排序列表 这是我的。首先我有无尾递归的方法 merge2 :: Ord a => [a] -> [a] -> [a] merge2 l1 [] = l1 merge2 [] l2 = l2 merge2 (x:xs) (y:ys) | x > y = y : merge2 (x:xs) ys | x < y = x : merge2 xs (y:ys)

我正在尝试编写一种尾部递归方法,将两个排序列表合并为一个排序列表

这是我的。首先我有无尾递归的方法

merge2 :: Ord a => [a] -> [a] -> [a]
merge2 l1 [] = l1
merge2 [] l2 = l2
merge2 (x:xs) (y:ys) | x > y = y : merge2 (x:xs) ys
                     | x < y = x : merge2 xs (y:ys)
                     | otherwise = x : merge2 (x:xs) ys

mergeTail :: Ord a => [a] -> [a] -> [a]
mergeTail accum [] = accum
mergeTail accum (x:xs) = mergeTail (x:accum) xs

当我输入像merge2Tail[1,2][2,3,4]这样的东西时,我希望得到[1,2,2,3,4]作为输出,但我得到的是随机顺序。我不确定在何处或如何实现在保持尾部递归的同时检查顺序的案例。

首先,让我们看看非尾部递归是什么样子:

merge :: Ord a => [a] -> [a] -> [a]
merge xs []                                 = xs
merge [] ys                                 = ys
merge fullXs@(x:xs) fullYs@(y:ys)  | x <= y = x : merge xs fullYs
                                   | x > y  = y : merge fullXs ys
因此,功能将是:

mergeTail :: Ord a => [a] -> [a] -> [a]
mergeTail xs [] = xs
mergeTail [] ys = ys
mergeTail xs ys = mergeAccum [] xs ys


mergeAccum :: Ord a => [a] -> [a] -> [a] -> [a]
mergeAccum acc [] []                       = acc
mergeAccum acc [] (y:ys)                   = mergeAccum (acc ++ [y]) [] ys
mergeAccum acc (x:xs) []                   = mergeAccum (acc ++ [x]) xs []
mergeAccum acc (x:xs) (y:ys)  | x <= y     = mergeAccum (acc ++ [x]) xs (y:ys)
                              | x > y      = mergeAccum (acc ++ [y]) (x:xs) ys
注:


在这种情况下,此函数的效率很低,因为每次递归调用都必须转到accum列表的末尾

首先,让我们看看非尾部递归的样子:

merge :: Ord a => [a] -> [a] -> [a]
merge xs []                                 = xs
merge [] ys                                 = ys
merge fullXs@(x:xs) fullYs@(y:ys)  | x <= y = x : merge xs fullYs
                                   | x > y  = y : merge fullXs ys
因此,功能将是:

mergeTail :: Ord a => [a] -> [a] -> [a]
mergeTail xs [] = xs
mergeTail [] ys = ys
mergeTail xs ys = mergeAccum [] xs ys


mergeAccum :: Ord a => [a] -> [a] -> [a] -> [a]
mergeAccum acc [] []                       = acc
mergeAccum acc [] (y:ys)                   = mergeAccum (acc ++ [y]) [] ys
mergeAccum acc (x:xs) []                   = mergeAccum (acc ++ [x]) xs []
mergeAccum acc (x:xs) (y:ys)  | x <= y     = mergeAccum (acc ++ [x]) xs (y:ys)
                              | x > y      = mergeAccum (acc ++ [y]) (x:xs) ys
注:


在这种情况下,此函数的效率很低,因为每次递归调用都必须转到accum列表的末尾

正如其他人所指出的,这是一个高度人工的练习:在Haskell中不应该递归地合并两个排序列表以形成一个排序列表。但是,如果您在一个调用堆栈大小非常有限且不支持尾部递归模cons的语言环境中构建一个严格的spined列表,那么这样做可能是合理的。在这种环境下,通常最好将此类问题分为两部分:

浏览列表,反向构建一个列表作为累加器

使用累加器生成最终结果

让我们从这个开始

merge :: Ord a => [a] -> [a] -> [a]
merge = \xs ys -> go [] xs ys
  where
    go acc [] ys = reverse acc ++ ys
    go acc xs [] = reverse acc ++ xs
    go acc xss@(x : xs) yss@(y : ys)
      | x <= y = go (x : acc) xs yss
      | otherwise = go (y : acc) xss ys
最后,

merge :: Ord a => [a] -> [a] -> [a]
merge = \xs ys -> go [] xs ys
  where
    go acc [] ys = acc `reverseOnto` ys
    go acc xs [] = acc `reverseOnto` xs
    go acc xss@(x : xs) yss@(y : ys)
      | x <= y = go (x : acc) xs yss
      | otherwise = go (y : acc) xss ys

我相信这是在你的约束下所能做的最好的事情。

正如其他人所指出的,这是一个高度人工的练习:在Haskell中不应该递归地合并两个排序列表以形成一个排序列表。但是,如果您在一个调用堆栈大小非常有限且不支持尾部递归模cons的语言环境中构建一个严格的spined列表,那么这样做可能是合理的。在这种环境下,通常最好将此类问题分为两部分:

浏览列表,反向构建一个列表作为累加器

使用累加器生成最终结果

让我们从这个开始

merge :: Ord a => [a] -> [a] -> [a]
merge = \xs ys -> go [] xs ys
  where
    go acc [] ys = reverse acc ++ ys
    go acc xs [] = reverse acc ++ xs
    go acc xss@(x : xs) yss@(y : ys)
      | x <= y = go (x : acc) xs yss
      | otherwise = go (y : acc) xss ys
最后,

merge :: Ord a => [a] -> [a] -> [a]
merge = \xs ys -> go [] xs ys
  where
    go acc [] ys = acc `reverseOnto` ys
    go acc xs [] = acc `reverseOnto` xs
    go acc xss@(x : xs) yss@(y : ys)
      | x <= y = go (x : acc) xs yss
      | otherwise = go (y : acc) xss ys

我相信这是在你的约束下你能做的最好的了。

你知道如何以非尾部递归的方式来做吗?是的,我应该编辑这个并包含它吗?这听起来相当低效。为什么希望它是尾部递归的?@Larry.Fish那么您需要三个参数,l1和l2以及累加器。是的,基于累加器的解决方案使用带有附加参数的辅助函数。最初,它可以是一个空列表。递归时,将要输出到累加器的元素前置。最后,累加器将以相反的顺序包含所需列表。您知道如何以非尾部递归的方式进行此操作吗?是的,我应该编辑此列表并将其包括在内吗?这听起来相当低效。为什么希望它是尾部递归的?@Larry.Fish那么您需要三个参数,l1和l2以及累加器。是的,基于累加器的解决方案使用带有附加参数的辅助函数。最初,它可以是一个空列表。递归时,将要输出到累加器的元素前置。最后,累加器将以相反的顺序包含通缉名单。对于这种情况,标准的补救方法是反向构建累加器,使用有效:,并在返回之前在基本情况下反转累积的结果。@Willenss我不太理解您的概念,它应该是什么?我不知道你改变了mergeAccum acc++[y]。。。在mergeAccum y:acc的递归子句中。。。而您更改了基本case子句…=acc进入…=逆向:这种情况下的标准补救方法是反向构建累加器,使用有效:,并在返回之前反向基本情况下的累积结果。@Willenss我不太理解你的概念,它应该是什么?我不知道你改变了mergeAccum acc++[y]。。。在mergeAccum y:acc的递归子句中。。。而您更改了基本case子句…=acc进入…=reverse acc:顺便说一句,reverseOnto是Lisps中非常常见的一种范例,我一直认为它甚至有一个内置的,revappend之类的;或者应该是,如果没有的话。当然,我们也可以在Haskell中使用flip$foldl’flip:顺便说一句,reverseOnto是Lisps中非常常见的一个范例,我一直认为它甚至有一个内置的,revappend之类的;或者应该是,如果没有的话。当然,我们也可以在Haskell中使用flip$foldl’flip: