繁体   English   中英

用递归对 Haskell 中的整数列表进行排序算法

[英]Sort algorithm for list of integers in Haskell with recursion

我需要在 haskell 上对一个整数列表进行排序,从小到大,但我不知道从哪里开始。

递归语法对我来说有点困难

一点点帮助会很棒。

我已经这样做了,但它并没有解决我的问题:

ordenarMemoria :: [Int] -> [Int]
ordenarMemoria [] = []
ordenarMemoria (x:y:xs) 
    | y > x = ordenarMemoria (y:xs)
    | otherwise = ordenarMemoria (x:xs)

谢谢

您尝试在正确的轨道上进行冒泡排序,这是排序的良好起点。 一些注意事项:

您处理列表为空或至少有两个元素( xy )的情况,但您忘记了列表只有一个元素时的情况。 您总是会遇到这种情况,因为您是在较小的列表上递归调用您的函数。

ordenarMemoria [x] = -- how do you sort a 1-element list?

第二个注意事项:在这个模式中

ordenarMemoria (x:y:xs) 
    | y > x = ordenarMemoria (y:xs)
    | otherwise = ordenarMemoria (x:xs)

您正在对以两个元素xy开头的列表进行排序。 您将xy进行比较,然后在删除两个元素之一后对列表的其余部分进行排序。 这一切都很好。

我的问题是:另一个元素发生了什么? 排序列表必须具有与输入相同的所有元素,因此您应该在输出中同时使用xy 所以在:

    | y > x = ordenarMemoria (y:xs)

你已经忘记了x 考虑

    | y > x = x : ordenarMemoria (y:xs)

这表明输出x ,然后是排序的余数。

另一个分支也忘记了其中一个输入。

修复该函数后,您可能会注意到列表的排序变得更加有序,但仍然没有完全排序。 这是冒泡排序的一个特性——您可能需要多次运行它。

我强烈建议你阅读了解你一个Haskell,有一个在线版本在这里,它有一个章节,你可以学习如何使用递归,就像快速排序例如列表进行排序:

quicksort :: (Ord a) => [a] -> [a]  
quicksort [] = []  
quicksort (x:xs) =   
    let smallerSorted = quicksort [a | a <- xs, a <= x]  
        biggerSorted = quicksort [a | a <- xs, a > x]  
    in  smallerSorted ++ [x] ++ biggerSorted  

我需要对整数列表进行排序

如何Data.List sort

$ stack ghci
Prelude> :m + Data.List
Prelude Data.List> sort [2,3,1]
[1,2,3]

有很多选择。 我通常建议从 Haskell 中的自底向上归并排序开始,但堆排序也不是一个糟糕的选择。 快速排序带来了更严重的困难。

-- Given two lists, each of which is in increasing
-- order, produce a list in increasing order.
--
-- merge [1,4,5] [2,4,7] = [1,2,4,4,5,7]
merge :: Ord a => [a] -> [a] -> [a]
merge [] ys = ???
merge xs [] = ???
merge (x : xs) (y : ys)
  | x <= y = ???
  | otherwise = ???

-- Turn a list of elements into a list of lists
-- of elements, each of which has only one element.
--
-- splatter [1,2,3] = [[1], [2], [3]]
splatter :: [a] -> [[a]]
splatter = map ????

-- Given a list of sorted lists, merge the adjacent pairs of lists.
-- mergePairs [[1,3],[2,4],[0,8],[1,2],[5,7]]
--   = [[1,2,3,4],[0,1,2,8],[5,7]]
mergePairs :: Ord a => [[a]] -> [[a]]
mergePairs [] = ????
mergePairs [as] = ????
mergePairs (as : bs : more) = ????

-- Given a list of lists of sorted lists, merge them all
-- together into one list.
--
-- mergeToOne [[1,4],[2,3]] = [1,2,3,4]
mergeToOne :: Ord a => [[a]] -> [a]
mergeToOne [] = ???
mergeToOne [as] = ???
mergeToOne lots = ??? -- use mergePairs here

mergeSort :: Ord a => [a] -> [a]
mergeSort as = ???? -- Use splatter and mergeToOne

一旦你填补了上面的空白,尝试通过使splatter产生两个或三个元素的排序列表而不是单例来优化排序。

这是经过修改的快速排序或插入排序。 它使用最快的方法为输出列表添加前缀或后缀值。 如果下一个值小于或大于列表的第一个或最后一个,则将其简单地附加到列表的开头或结尾。 如果该值不小于head值或大于last值,则必须插入它。 插入与上面所谓的快速排序是相同的逻辑。

现在,踢球者。 此函数作为foldr函数运行只是为了降低函数的复杂性。 它可以很容易地转换为递归函数,但它与foldr一起运行良好。

f2x :: (Ord a) => a -> [a] -> [a]
f2x n ls
   | null ls = [n]
   | ( n <= (head ls) ) = n:ls -- ++[11]
   | ( n >= (last ls) ) = ls ++ [n] -- ++ [22]
   | True = [lx|lx <-ls,n > lx]++ n:[lx|lx <-ls,n < lx]

可以删除两行之后的注释,并且可以使用scanr运行该函数以查看有多少命中具有简单的前缀或后缀值,以及插入到第一个或最后一个值以外的其他位置。

foldr f2x [] [5,4,3,2,1,0,9,8,7,6]

或者af = foldr a2x [] ... af [5,4,3,2,1,0,9,8,7,6] >-> [0,1,2,3,4,5,6, 7,8,9]

编辑 5/18/2018

Stack Overflow最棒的地方是像 @dfeuer 这样让你思考的人。 @dfeuer 建议使用partition 我像个孩子,不知道怎么办。 我表达了我在partition困难,但 @dfeuer 强迫我了解如何使用它。 @dfeuer 还指出,在上述函数中使用last是一种浪费。 我也不知道。

以下函数使用从Data.List导入的partition partition输出一个元组对。 此函数也适用于foldr 它是一个完整的插入排序功能。

ft nv ls = b++[nv]++e where (b,e) = partition (<=nv) ls

像上面一样使用它

foldr ft [] [5,4,3,2,1,0,9,8,7,6]

Haskell 和函数式编程都是关于在其他函数中使用现有函数。

putEleInSortedListA :: Ord a => a -> [a] -> [a]
putEleInSortedListA a [] = [a]
putEleInSortedListA a (b:bs)
  | a < b     = a : b : bs
  | otherwise = b: putEleInSortedListA a bs

sortListA :: Ord a => [a] -> [a]
sortListA la = foldr (\a b -> putEleInSortedListA a b) [] la

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM