Haskell 设置类似于维护插入顺序的数据结构? 我要找的房产是 最初保持插入顺序 按插入顺序横切 当然,每个元素都是独一无二的 但在某些情况下,可以忽略插入顺序,例如。。。 检索两个不同集合之间的差异 执行两个集合的并集以消除任何重复项

Haskell 设置类似于维护插入顺序的数据结构? 我要找的房产是 最初保持插入顺序 按插入顺序横切 当然,每个元素都是独一无二的 但在某些情况下,可以忽略插入顺序,例如。。。 检索两个不同集合之间的差异 执行两个集合的并集以消除任何重复项,haskell,data-structures,Haskell,Data Structures,似乎正是我想要的,除了它不是用Haskell写的 当前和初始解决方案 最简单(也是相对低效)的解决方案是将其实现为一个列表,并在需要时将其转换为一个集合,但我相信可能有更好的方法 其他想法 我的第一个想法是将其实现为数据。将的新类型的设置为(Int,a),其中它将按第一个元组索引排序,第二个索引(a)为实际值。我很快意识到这是行不通的,因为集合允许重复类型a,这会破坏使用集合的整个目的 同时维护列表和集合?(没有) 我的另一个想法是有一个抽象的数据类型,可以同时维护数据的列表和集合表示,这听起来

似乎正是我想要的,除了它不是用Haskell写的

当前和初始解决方案 最简单(也是相对低效)的解决方案是将其实现为一个列表,并在需要时将其转换为一个集合,但我相信可能有更好的方法

其他想法 我的第一个想法是将其实现为
数据。将
新类型的
设置为
(Int,a)
,其中它将按第一个元组索引排序,第二个索引(
a
)为实际值。我很快意识到这是行不通的,因为集合允许重复类型
a
,这会破坏使用集合的整个目的

同时维护列表和集合?(没有) 我的另一个想法是有一个抽象的数据类型,可以同时维护数据的列表和集合表示,这听起来也不太有效

扼要重述
在Haskell中有这样的数据结构的实现吗?我见过,但它似乎只是将集合操作添加到列表中,这听起来也非常低效(但如果找不到解决方案,我可能会解决这个问题)。另一个建议的解决方案是通过实现它,但如果它已经解决了问题,我更愿意不重新实现它。

您当然可以使用
数据。设置
(Int,a)
同构,但包装在一个新类型中,具有不同的
Eq
实例:

newtype Entry a = Entry { unEntry :: (Int, a) } deriving (Show)

instance Eq a => Eq (Entry a) where
    (Entry (_, a)) == (Entry (_, b)) = a == b

instance Ord a => Ord (Entry a) where
    compare (Entry (_, a)) (Entry (_, b)) = compare a b
但是,如果您希望索引自动递增,那么这并不能完全解决所有问题,因此您可以围绕
(Set(Entry a),Int)

但这确实意味着您必须重新实现
数据。设置
以尊重此关系:

import qualified Data.Set as S
import Data.Set (Set)
import Data.Ord (comparing)
import Data.List (sortBy)

-- declarations from above...

null :: IndexedSet a -> Bool
null (IndexedSet (set, _)) = S.null set

-- | If you re-index on deletions then size will just be the associated index
size :: IndexedSet a -> Int
size (IndexedSet (set, _)) = S.size set

-- Remember that (0, a) == (n, a) for all n
member :: Ord a => a -> IndexedSet a -> Bool
member a (IndexedSet (set, _)) = S.member (Entry (0, a)) set

empty :: IndexedSet a
empty = IndexedSet (S.empty, 0)

-- | This function is critical, you have to make sure to increment the index
--   Might also want to consider making it strict in the i field for performance
insert :: Ord a => a -> IndexedSet a -> IndexedSet a
insert a (IndexedSet (set, i)) = IndexedSet (S.insert (Entry (i, a)) set, i + 1)

-- | Simply remove the `Entry` wrapper, sort by the indices, then strip those off
toList :: IndexedSet a -> [a]
toList (IndexedSet (set, _))
    = map snd
    $ sortBy (comparing fst)
    $ map unEntry
    $ S.toList set
但在大多数情况下,这相当简单,您可以根据需要添加功能。你唯一需要真正担心的是删除时该怎么做。你是对所有的东西重新编制索引,还是只关心顺序?如果您只关心顺序,那么它很简单(而且
size
可以通过实际计算基础
集的大小来保持次优状态,但是如果您重新编制索引,那么您可以在
O(1)
时间内得到您的大小。这些类型的决定应该根据您试图解决的问题来决定


如果它已经是一个解决了的问题,我宁愿不重新实现它


这种方法无疑是一种重新实施。但在大多数情况下,它并不复杂,可以很容易地变成一个很好的小库,上传到Hackage,并保留了很多集合的优点,而无需太多的簿记

a适合您的目的吗?可能不适合,元素的唯一性是关键,而堆栈并不能保证这一点。当然,除非我手动检查唯一性(这不是理想的),否则我也认为您应该使用单向注释树,如finger树解决方案中所建议的(您也可以对其他树执行此操作,例如我的splaytree包)。对于非fingertree树,幺半群可以是
(Max Int,Set val)
,这将允许您维护插入顺序(通过
Max Int
)并减少重复(通过
Set val
)。然后将有O(logn)插入和查找,以及O(n)遍历。(没有具体实现,但它展示了地图的技术)哦,好的,我知道你在做什么,这是一个更好的解决方案。我读它的时候就像“等式与排序断开会不会导致像插入和成员这样的操作具有
O(N)
complexity?”,但后来注意到您也是按元素而不是索引排序的。@ABot让
Eq
Ord
实例密切相关总是一个好主意,这就是我在这种情况下选择做的。索引实际上只在插入时使用,并且在转换回列表时使用,以便所有内容都按您放入的顺序显示,但除此之外,它本质上只是无意义的元数据。这与您不喜欢的原因相同,即个人偏好。我可以接受用一种新的数据类型来看待这一点,但我确实相信,根据所执行的优化,开销会稍大一些。我知道对于新类型和元组,ghc有特殊的优化规则。@chi我还想说,使用
newtype
s有助于表明解决方案存在于与元组同构的类型中,只是具有不同的行为。由于这正是
newtype
s的目的,我认为它比使用
data
类型更好。这种方法的一个缺点是,为了按插入顺序进行遍历,必须通过列表(或按索引对数据排序),这将增加使用(列表,集合)对的复杂性。但也许更重要的是进行有效的删除。
import qualified Data.Set as S
import Data.Set (Set)
import Data.Ord (comparing)
import Data.List (sortBy)

-- declarations from above...

null :: IndexedSet a -> Bool
null (IndexedSet (set, _)) = S.null set

-- | If you re-index on deletions then size will just be the associated index
size :: IndexedSet a -> Int
size (IndexedSet (set, _)) = S.size set

-- Remember that (0, a) == (n, a) for all n
member :: Ord a => a -> IndexedSet a -> Bool
member a (IndexedSet (set, _)) = S.member (Entry (0, a)) set

empty :: IndexedSet a
empty = IndexedSet (S.empty, 0)

-- | This function is critical, you have to make sure to increment the index
--   Might also want to consider making it strict in the i field for performance
insert :: Ord a => a -> IndexedSet a -> IndexedSet a
insert a (IndexedSet (set, i)) = IndexedSet (S.insert (Entry (i, a)) set, i + 1)

-- | Simply remove the `Entry` wrapper, sort by the indices, then strip those off
toList :: IndexedSet a -> [a]
toList (IndexedSet (set, _))
    = map snd
    $ sortBy (comparing fst)
    $ map unEntry
    $ S.toList set