在以下代码段中:
import qualified Data.Set as Set
data Nat = Zero | Succ Nat deriving (Eq, Show, Ord)
instance Enum Nat where
pred (Succ x) = x
succ x = Succ x
toEnum 0 = Zero
toEnum x = Succ (toEnum (x-1))
fromEnum Zero = 0
fromEnum (Succ x) = 1 + (fromEnum x)
nats :: [Nat]
nats = [Zero ..]
natSet :: Set.Set Nat
natSet = Set.fromList nats
为什么:
elem (toEnum 100) nats
== True
但
Set.member (toEnum 100) natSet
永远不会结束?答案 0 :(得分:15)
现有答案已经足够,但我想对Set
s的行为进行一点阐述。
看起来你希望所有Nat
的懒惰集合;你获取所有Nat
的无限列表并在其上使用Set.toList
。那样就好了;数学家经常谈论“所有自然数的集合”。问题是Set
的实现并不像列表那样适应懒惰。
Set的实现基于大小平衡的二叉树(或 有界平衡的树木)
假设您希望从列表中懒惰地构造二叉树。当需要对树进行更深的遍历时,列表中的元素将仅插入到树中。那么你问一下100是否在树上。它会在树上添加1-99号,一次一个。然后它最终将100添加到树中,并发现100确实是树中的元素。但请注意我们做了什么。我们刚刚执行了懒惰列表的有序遍历!所以第一次,我们的虚构LazyTree.contains
将具有与List.find
大致相同的复杂度(假设一个转移的 O(1)插入,这对于简单二进制文件来说是一个错误的假设树,其 O(log n)复杂度。如果没有平衡,我们的树就会非常不平衡(我们按顺序添加数字1到100,所以它只是每个分支的正确子项下的一个大链表)。但是在遍历期间使用树平衡,很难知道再次开始遍历的位置;至少它肯定不会立即直观。
tl;博士:没人(afaik)已经做了一个很好的懒人套装。因此,无限集合现在更容易表示为无限列表。
答案 1 :(得分:8)
Set.fromList
不是懒惰的,所以如果你把它传递给无限列表它就不会结束。但是natSet
在需要之前不会被构建,所以当你在它上面运行Set.member
时,你才会注意到它。
例如,即使Set.null $ Set.fromList [0..]
也不会终止。
答案 2 :(得分:4)
你不能拥有无限集。这不仅会影响Set.member
,每当您执行任何会导致natSet
进行评估甚至一步(甚至Set.null
)的任何事情时,它都将进入无限循环。
答案 3 :(得分:1)
让我们看看当我们调整GHC's Set code以适应无限集时会发生什么:
module InfSet where
data InfSet a = Bin a (InfSet a) (InfSet a)
-- create an infinite set by unfolding a value
ofUnfold :: (x -> (x, a, x)) -> x -> InfSet a
ofUnfold f x =
let (lx,a,rx) = f x
l = ofUnfold f lx
r = ofUnfold f rx
in Bin a l r
-- check for membership in the infinite set
member :: Ord a => a -> InfSet a -> Bool
member x (Bin y l r) = case compare x y of
LT -> member x l
GT -> member x r
EQ -> True
-- construct an infinite set representing a range of numbers
range :: Fractional a => (a, a) -> InfSet a
range = ofUnfold $ \(lo,hi) ->
let mid = (hi+lo) / 2
in ( (lo, mid), mid, (mid, hi) )
请注意,不是从无限列表构造无限集,
我改为定义一个函数ofUnfold
来将单个值展开到无限列表中。
它允许我们平行地构建两个分支(我们不需要完成
在构建另一个分支之前的一个分支。)
让我们给它一个旋转:
ghci> :l InfSet
[1 of 1] Compiling InfSet ( InfSet.hs, interpreted )
Ok, modules loaded: InfSet.
ghci> let r = range (0,128)
ghci> member 64 r
True
ghci> member 63 r
True
ghci> member 62 r
True
ghci> member (1/2) r
True
ghci> member (3/4) r
True
嗯,这似乎有效。如果我们在Set之外尝试一个值会怎么样?
ghci> member 129 r
^CInterrupted.
这将只是运行并运行,永不退出。在无限集中没有停止分支, 所以搜索永远不会退出。我们可以以某种方式检查原始范围,但这对于无限的离散元素集是不实用的:
ghci> let ex = ofUnfold (\f -> ( f . (LT:), f [EQ], f . (GT:) )) id
ghci> :t ex
ex :: InfSet [Ordering]
ghci> member [EQ] ex
True
ghci> member [LT,EQ] ex
True
ghci> member [EQ,LT] ex
^CInterrupted.
所以无限集可能但我不确定它们是有用的。
答案 4 :(得分:0)
我有同样的感觉,所以我添加了一个与无限列表一起使用的集合。但是它们需要进行排序,因此我的算法知道何时停止寻找更多。
Prelude> import Data.Set.Lazy
Prelude Data.Set.Lazy> natset = fromList [1..]
Prelude Data.Set.Lazy> 100 `member` natset
True
Prelude Data.Set.Lazy> (-10) `member` natset
False
它的hackage。 http://hackage.haskell.org/package/lazyset-0.1.0.0/docs/Data-Set-Lazy.html