关于效率我不知道该说什么,但我们可以分解正在发生的事情并至少获得几个不同的功能。特定的功能可能是可优化的,但重要的是要明确需要什么。
让我重新表述这个问题:对于一些集合 X,一些二元关系 R,以及一些二元运算 +,产生一个集合 Q = {x+y | X 中的 x,X 中的 y,xRy}。因此,对于您的示例,我们可能有 X 是一组列表, R 是“当且仅当 x 和 y 中至少有一个元素时 xRy”,而 + 是++
。
一个幼稚的实现可能只是复制 set-builder 符号本身
shareElement :: Eq a => [a] -> [a] -> Bool
shareElement xs ys = or [x == y | x <- xs, y <- ys]
v1 :: (a -> a -> Bool) -> (a -> a -> b) -> [a] -> [b]
v1 (?) (<>) xs = [x <> y | x <- xs, y <- xs, x ? y]
那么p = v1 shareElement (++) :: Eq a => [[a]] -> [[a]]
可能会达到你想要的。除非它可能没有。
Prelude> p [[1], [1]]
[[1,1],[1,1],[1,1],[1,1]]
最明显的问题是我们得到了四个副本:两个来自将列表与其自身合并,两个来自“双向”合并列表。出现问题是因为List
与Set
我们不能杀死唯一性不同。当然,这很容易解决,我们将在Set
任何地方使用
import Data.Set as Set
v2 :: (a -> a -> Bool) -> (a -> a -> b) -> Set.Set a -> Set.Set b
v2 (?) (<>) = Set.fromList . v1 (?) (<>) . Set.toList
所以我们可以再试p = v2 (shareElement
一次Set.toList) Set.union
Prelude Set> p $ Set.fromList $ map Set.fromList [[1,2], [2,1]]
fromList [fromList [1,2]]
这似乎有效。请注意,我们必须“通过” List
,因为Set
不能作为实例Monad
或Applicative
由于其Ord
约束。
我还注意到在Set
. x <> y
例如,y <> x
当我们的关系是对称的时,我们要么丢弃列表中的订单信息,要么不得不同时处理这两者。
一些更方便的版本可以写成
v3 :: Monoid a => (a -> a -> Bool) -> [a] -> [a]
v3 r = v2 r mappend
如果我们假设关系是,比方说,从那时起的等式关系,我们可以构建更有效的关系,而不是进行O(n^2)
操作,我们可以在O(nd)
其中d
执行关系的分区(陪集)的数量。
一般来说,这是一个非常有趣的问题。