Sorting 不使用Prelude以外的任何东西对文件系统数据进行排序
我通过以下方式定义了一种称为FS的数据类型:Sorting 不使用Prelude以外的任何东西对文件系统数据进行排序,sorting,haskell,Sorting,Haskell,我通过以下方式定义了一种称为FS的数据类型: type Name= String data Ext where { Txt::Ext ; Mp3::Ext ; Jar::Ext ; Doc::Ext ; Hs::Ext } deriving (Eq, Show) data FS where { A :: (Name,Ext) -> FS; Dir :: Name-> [FS] -> FS } deriving (Eq, Show
type Name= String
data Ext where { Txt::Ext ; Mp3::Ext ; Jar::Ext ; Doc::Ext ; Hs::Ext }
deriving (Eq, Show)
data FS where { A :: (Name,Ext) -> FS;
Dir :: Name-> [FS] -> FS }
deriving (Eq, Show)
(A代表文件,Dir代表目录)我试图创建一个函数,给定一个
FS
(目录),它返回相同的FS
,但在所有级别按字母顺序排列,我目前的尝试如下:
orderFS :: FS-> FS
orderFS (Dir x y) = Dir x (map orderFS (sort y));
orderFS (A (x,y)) = A (x,y);
我唯一缺少的是一个名为“sort”的函数,它接受一个[FS]
,并按名称
字段的字母顺序返回它。我读到有一些函数,比如从
Data.List
排序,可以提供帮助,但我必须在不使用任何其他函数的情况下执行此操作,而不是使用Prelude
那么我应该如何实现这样的功能呢?提前感谢我不相信Prelude中有任何排序功能,但像
Data.List这样的模块中没有。请注意,Data.List
位于GHC的基本包中,因此基本上在Prelude
可用的任何情况下,我都可以想象Data.List
也可以——您不需要下载/包含任何其他包来使用它
也就是说,如果您确实想编写自己的排序函数,那么最好采用现有的简单排序算法并使用它。在Haskell中有非常简洁/简单的编写快速排序和合并排序的方法,尽管显而易见的实现有时并不像您预期的那样具有完全相同的性能特征。例如,Merge-sort具有大致相同的渐近性,但将列表分成两部分实际上需要一些时间,因为列表是单独链接的,因此必须遍历其中的一半才能将其拆分。但是,它可以是一个非常好的短函数,看起来非常像算法,并且可能值得作为一个学习练习来做
另外,我注意到您正在将Ext
和FS
类型定义为GADT,我并不确定其动机;我建议使用非GADT语法,这在本例中要简单得多:
type Name = String
data Ext = Txt | Mp3 | Jar | Doc | Hs deriving (Eq, Show)
data FS = A Name Ext | Dir Name [FS] deriving (Eq, Show)
为了按名称对它们进行排序,可能需要编写一个简单的访问器函数来获取FS的名称:
name :: FS -> Name
name (A n _) = n
name (Dir n _) = n
另一种方法是找出这两种情况的共同点(名称):
data FS = NamedFS { name :: Name, fs :: UnnamedFS }
data UnnamedFS = A Ext | Dir [FS]
这里的第一个条目使用记录语法,其中包括自动生成name::FS->name
访问器以及FS::FS->UnnamedFS
对于实际排序,它看起来很像合并排序的算法描述。首先,让我们编写一个函数将列表一分为二:
split :: [a] -> ([a], [a])
split xs = splitAt (length xs `div` 2) xs
我们还需要一个函数来合并两个排序列表:
merge :: Ord a => [a] -> [a] -> [a]
merge [] x = x
merge x [] = x
merge (x:xs) (y:ys) | x < y = x:merge xs (y:ys)
| otherwise = y:merge (x:xs) ys
现在,我们可以像往常一样实现mergesort:
mergeSort :: (a -> a -> Bool) -> [a] -> [a]
mergeSort _ [] = []
mergeSort _ [x] = [x]
mergeSort f l = merge f (mergeSort f left) (mergeSort f right)
where (left, right) = split l
就这样说吧:
-- fss is some [FS]
mergeSort (\x y -> name x < name y) fss
我不相信Prelude中有任何排序功能,但像Data.List
这样的模块中没有。请注意,Data.List
位于GHC的基本包中,因此基本上在Prelude
可用的任何情况下,我都可以想象Data.List
也可以——您不需要下载/包含任何其他包来使用它
也就是说,如果您确实想编写自己的排序函数,那么最好采用现有的简单排序算法并使用它。在Haskell中有非常简洁/简单的编写快速排序和合并排序的方法,尽管显而易见的实现有时并不像您预期的那样具有完全相同的性能特征。例如,Merge-sort具有大致相同的渐近性,但将列表分成两部分实际上需要一些时间,因为列表是单独链接的,因此必须遍历其中的一半才能将其拆分。但是,它可以是一个非常好的短函数,看起来非常像算法,并且可能值得作为一个学习练习来做
另外,我注意到您正在将Ext
和FS
类型定义为GADT,我并不确定其动机;我建议使用非GADT语法,这在本例中要简单得多:
type Name = String
data Ext = Txt | Mp3 | Jar | Doc | Hs deriving (Eq, Show)
data FS = A Name Ext | Dir Name [FS] deriving (Eq, Show)
为了按名称对它们进行排序,可能需要编写一个简单的访问器函数来获取FS的名称:
name :: FS -> Name
name (A n _) = n
name (Dir n _) = n
另一种方法是找出这两种情况的共同点(名称):
data FS = NamedFS { name :: Name, fs :: UnnamedFS }
data UnnamedFS = A Ext | Dir [FS]
这里的第一个条目使用记录语法,其中包括自动生成name::FS->name
访问器以及FS::FS->UnnamedFS
对于实际排序,它看起来很像合并排序的算法描述。首先,让我们编写一个函数将列表一分为二:
split :: [a] -> ([a], [a])
split xs = splitAt (length xs `div` 2) xs
我们还需要一个函数来合并两个排序列表:
merge :: Ord a => [a] -> [a] -> [a]
merge [] x = x
merge x [] = x
merge (x:xs) (y:ys) | x < y = x:merge xs (y:ys)
| otherwise = y:merge (x:xs) ys
现在,我们可以像往常一样实现mergesort:
mergeSort :: (a -> a -> Bool) -> [a] -> [a]
mergeSort _ [] = []
mergeSort _ [x] = [x]
mergeSort f l = merge f (mergeSort f left) (mergeSort f right)
where (left, right) = split l
就这样说吧:
-- fss is some [FS]
mergeSort (\x y -> name x < name y) fss
Data.List
中可以帮助您的函数是
这与函数getName::FS->Name
一起允许您通过比较Name
s进行排序
如果无法使用Data.List
中的函数,则必须自己实现排序算法(其中有许多可供选择)。一个例子是本书中实现的“快速排序”:
Data.List
中可以帮助您的函数是
这与函数getName::FS->Name
一起允许您通过比较Name
s进行排序
如果无法使用Data.List
中的函数,则必须自己实现排序算法(其中有许多可供选择)。一个例子是本书中实现的“快速排序”:
在我看来,Haskell中最自然的列表排序函数是自下而上的mergesort(Peter Amidon的答案给出了自上而下的mergesort)。假设您从列表开始
[25,1,22,2,10,8,6,20,13,28,5,3,11]
第一步是将列表转换为列表列表。傻瓜
[[1,2,6,8,10,20,22,25],[3,5,11,13,28]]
[[1,2,3,5,6,8,10,11,13,20,22,25,28]]
infixr 5 :::
data LL a = ![a] ::: LL a | Nil
breakUp :: (a -> a -> Ordering) -> [a] -> LL a
breakUp _cmp [] = Nil
breakUp _cmp xs@[a] = xs ::: Nil
breakUp cmp (x1 : x2 : xs)
| GT <- cmp x1 x2 = [x2,x1] ::: breakUp cmp xs
| otherwise = [x1,x2] ::: breakUp cmp xs
merge :: (a -> a -> Ordering)
-> [a] -> [a] -> [a]
merge _cmp [] ys = ys
merge _cmp xs [] = xs
merge cmp xss@(x:xs) yss@(y:ys)
| GT <- cmp x y = y : merge cmp xss ys
| otherwise = x : merge cmp xs yss
mergePairwise :: (a -> a -> Ordering)
-> LL a -> LL a
mergePairwise _cmp Nil = Nil
mergePairwise _cmp xs@(_ ::: Nil) = xs
mergePairwise cmp (xs1 ::: xs2 ::: xss)
= merge cmp xs1 xs2 ::: mergePairwise cmp xss
mergeAll :: (a -> a -> Ordering)
-> LL a -> [a]
mergeAll _cmp Nil = []
mergeAll _cmp (xs ::: Nil) = xs
mergeAll cmp xss = mergeAll cmp (mergePairwise cmp xss)
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
sortBy cmp = mergeAll cmp . breakUp cmp
sort :: Ord a => [a] -> [a]
sort = sortBy compare
data NonEmpty a = a :| [a]
data LL a = (:::) {-# UNPACK #-} !(NonEmpty a) (LL a) | Nil