如何根据另一个列表(模板)对Haskell中的列表进行排序(重新定位其元素)?例如,我有2个列表:
[1,2,3]
[(2,'b'),(1,'a'),(3,'c')]
我想对第二个列表进行排序,以便每个元素的第一个对应于第一个列表的元素。即我希望它是[(1,'a'),(2,'b'),(3,'c')]
。
我写了一个非常不优雅的实现:
sortMimicking :: (Eq a) => [a] -> [(a, b)] -> [(a, b)]
sortMimicking template xs = filterJust $ r (reverse template) xs []
where filterJust [] = []
filterJust (x:xs) = case x of
Nothing -> filterJust xs
Just element -> element:filterJust xs
deleteWith comparison xs = [ x | x <- xs, not $ comparison x ]
r _ [] acc = acc
r (t:ts) xs acc = r ts
(deleteWith ((==t) . fst) xs)
(find ((==t) . fst) xs : acc)
它只适用于对的列表,所以我写了这个概括:
sortMimickingBy :: (Eq a) => (b -> a) -> [a] -> [b] -> [b]
sortMimickingBy c template xs = filterJust $ r (reverse template) xs []
where filterJust [] = []
filterJust (x:xs) = case x of
Nothing -> filterJust xs
Just element -> element:filterJust xs
deleteWith comparison xs = [ x | x <- xs, not $ comparison x ]
r _ [] acc = acc
r (t:ts) xs acc = r ts
(deleteWith ((==t) . c) xs)
(find ((==t) . c) xs : acc)
我尝试使用sortWith
模块中的GHC.Exts
来提出更优雅的功能,但无济于事。有a similar Python question。
答案 0 :(得分:2)
如果您允许Ord a
,则可以使用线性元素查找执行O(n log n)
解决方案而不是二次解决方案。我们的想法是记住模板列表元素的原始位置(通过压缩[0..]
),然后排序多次以对齐元素,然后恢复正确的顺序。
import Data.List
import Data.Ord
sortMimicking :: Ord a => [a] -> [(a, b)] -> [(a, b)]
sortMimicking guide xs = map snd $ sortBy (comparing (snd . fst)) $ zip guide' xs'
where
guide' = sortBy (comparing fst) $ zip guide [0..]
xs' = sortBy (comparing fst) xs
答案 1 :(得分:0)
试试这个:
import Data.List (sortBy, elemIndex)
import Data.Ord (comparing)
mySort :: [Int] -> [(Int,a)] -> [(Int,a)]
mySort as bs = sortBy (comparing go) bs
where go (a,b) = maybe 0 id (elemIndex a as)
示例:
ghci> mySort [3,2,1] [(1,'a'), (2,'b'), (3,'c')]
[(3,'c'),(2,'b'),(1,'a')]
基本上go (a,b)
通过比较每对中的a
来计算第一个列表中的sortBy (comparing go) ...
索引和go
排序。
注意:由于elemIndex
每次调用时都会从列表前面扫描,因此效率不高。