我想创建用于访问和修改简单音乐库的数据类型,包括由曲目组成的专辑。有关基本想法,请考虑以下事项:
data MusicCollection = MC { albums :: Seq Album }
data Album = Album { albumTitle :: String, tracks :: Seq Track }
data Track = Track { trackTitle :: String, tempo :: Tempo }
data Tempo = Unknown | BPM Int
除了节奏,还可能有其他属性,如样式或评级。
上述解决方案让我可以快速访问随机相册。另外,我希望能够比指定速度更快地访问所有曲目。再次,快速随机访问返回的曲目会很好:
fasterThan :: Int -> MusicCollection -> SomeRandomAccessCollection Track
更新集合中的曲目也不应该太慢。
我不知道向MusicCollection添加Map Tempo (Seq Track)
是否最好,或者是否可能以某种方式模仿关系数据库。也许有完全不同的解决方案?
我目前不想使用数据库,但了解何时在桌面应用程序中使用它们会很有趣。
答案 0 :(得分:6)
如果您不想使用数据库,那么您几乎必须为您希望快速访问的每个关系设置Map
,如您所述。
请注意Data.Map.Map
包中的containers
使用有序键,因此您可以使用Data.Map.split
来获得合理快速的地图分区,这有助于更快地找到“跟踪”比指定的速度“(假设您为Ord
派生Tempo
)。因此,如果不需要关系的有序键,则应使用Data.HashMap.HashMap
中的unordered-containers
代替,其实现速度更快。
也可以使用没有其他依赖关系的内存中关系数据库。例如,您可以使用persistent
和Sqlite作为后端。如何执行此操作在persistent tutorial。
答案 1 :(得分:3)
是的,Map Tempo (Seq Track)
在这里是一个不错的选择,特别是因为它基于Ord
的结构可让您有效地进行“节奏大于 n 的所有曲目”的查询;比照Data.Map.split
这基本上 通过使用索引来模仿关系数据库。
您可能也对IxSet和HiggsSet(不同作者对IxSet的预期继任者)感兴趣,这些内容旨在用各种索引扩充集合结构,特别是与事务相结合像acid-state这样的序列化系统。它们支持方便的比较查询,例如大于,小于等等。但是,如果你只有一个或两个索引,那么最好只是自己滚动。
答案 2 :(得分:3)
提及数据库是件好事,因为关系数据库的设计正是为了解决这些问题。数据库理论的典型例子是发票/项目问题,您的问题几乎完全反映出来:发票有单独的项目,但有时我们想要编写报告,考虑项目而不将它们连接到发票。强迫我们通过发票获取物品的数据库或数据结构使我们在这些情况下做了太多工作。
因此,您在此处的一个替代方案就是使用关系数据库(像SQLite这样的轻量级嵌入式数据库可能是最合适的)。另一个替代方案是创建一个更像关系的设计,并将问题建模为少数关系,它们可以通过多种方式相互连接 - 可能由一个加速加入的 index 。
所以你可以从这样的东西开始,其中曲目在集合的顶层可用(因此很容易只搜索曲目):
data MusicCollection = MC { tracks :: Set Track
, albums :: Set Album
, albumToTracks :: Album -> [Track] }
data Track = Track { album :: Album, trackTitle :: String, tempo :: Maybe Int }
deriving (Eq, Ord)
data Album = Album { albumTitle :: String }
deriving (Eq, Ord)
构建MusicCollection值时,您必须确保构建albumToTracks
函数,以便快速获取专辑的曲目。 (或者您可以使用Map Album [Track]
而不是函数;函数更通用。)
我还没有找到另一种选择,但这可能值得研究:圆形数据结构,其中曲目和专辑都互相引用。你有这样的数据声明:
data MusicCollection = MC { tracks :: Set Track, albums :: Set Album }
data Track = Track { album :: Album, trackTitle :: String, tempo :: Maybe Int }
deriving (Eq, Ord)
data Album = Album { albumTitle :: String, tracks :: Set Track }
deriving (Eq, Ord)
这里的问题当然是,当你构建这个数据结构时,你是否可以“打结”,以便它可以有限地表示。如果你可以把它拉下来,那么这将是很棒的......