是否有可能在haskell中有一套理解?

时间:2018-02-18 09:43:29

标签: haskell set list-comprehension

在Haskell中我们有列表生成器,例如:

[x+y | x<-[1,2,3], y<-[1,2,3]]

我们得到

[2,3,4,3,4,5,4,5,6]

如果元素已经在列表中,是否有可能没有自动添加元素的集合生成器?

在我们的例子中,我们将获得:

[2,3,4,5,6]

如果是这样,怎么样?如果它尚未实现,您将如何实现它?

2 个答案:

答案 0 :(得分:15)

Haskell可以做到这一点,但不是完全开箱即用。

基本的基础是列表理解也可以写成monadic绑定链,在列表monad中:

Prelude> [x+y | x<-[1,2,3], y<-[1,2,3]]
[2,3,4,3,4,5,4,5,6]
Prelude> [1,2,3] >>= \x -> [1,2,3] >>= \y -> return (x+y)
[2,3,4,3,4,5,4,5,6]

...或者,具有更好的可读性do - 语法(这是monadic绑定的语法糖)

Prelude> do x<-[1,2,3]; y<-[1,2,3]; return (x+y)
[2,3,4,3,4,5,4,5,6]

事实上,有一种语言扩展也将所有列表理解转化为这种monadic链的语法糖。元组(又名作家)monad中的示例:

Prelude> :set -XMonadComprehensions 
Prelude> [x+y | x <- ("Hello", 4), y <- ("World", 5)] :: (String, Int)
("HelloWorld",9)

所以,我们所需要的只是设置monad 。这是明智的,但是Data.Set.Set Hask (所有Haskell类型的类别)上的monad,但只有满足{{1的子类别的子类别约束(查找/避免重复所需)。在集合的情况下,有一个hack允许从实际的monad实例隐藏该约束;它在set-monad package中使用。 Etvoilà:

Ord

Prelude Data.Set.Monad> [x+y | x<-fromList[1,2,3], y<-fromList[1,2,3]] fromList [2,3,4,5,6] 所需的黑客是有代价的。它的工作原理如下:

instance Monad Set

这意味着:类型{-# LANGUAGE GADTs, RankNTypes #-} import qualified Data.Set as DS data Set r where Prim :: (Ord r => DS.Set r) -> Set r Return :: a -> Set a Bind :: Set a -> (a -> Set b) -> Set b ... 的值实际上并不包含一组具体的整数。相反,它包含一个抽象语法表达式,用于计算结果。这对于性能来说并不是很好,特别是它意味着不会共享值。所以不要将它用于大集。

有一个更好的选择:直接将其用作正确类别中的monad (其中仅包含可订购的类型)。不幸的是,这需要更多语言弯曲,但这是可能的;我做了一个例子in the constrained-categories library

答案 1 :(得分:5)

如果您的值可以放入Data.Set.Set(即他们在课堂Ord中),您只需将Data.Set.toList . Data.Set.fromList应用于您的列表:

Prelude> import Data.Set
Prelude Data.Set> Data.Set.toList . Data.Set.fromList $ [x+y | x<-[1,2,3], y<-[1,2,3]]
[2,3,4,5,6]

复杂性为O(n log n)

如果类型服从(Eq a, Hashable a),您可以以相同的方式使用Data.HashSet。平均复杂度为O(n)

如果您拥有的只是Eq,则必须使用类似Data.List.nub的内容:

Prelude> import Data.List
Prelude Data.List> nub [x+y | x<-[1,2,3], y<-[1,2,3]]
[2,3,4,5,6]

但复杂性本质上是二次的。