一个解决方法可能是使用递增边界。我们先稍微放宽一下问题:允许输出重复的值。在这种情况下,可以使用以下方法:
import Data.List (intersect)
intersectInfinite :: Eq a => [a] -> [a] -> [a]
intersectInfinite = intersectInfinite' 1
where intersectInfinite' n = intersect (take n xs) (take n ys) ++ intersectInfinite' (n+1)
换句话说,我们声称:
A∩B = A1∩B1 ∪ A2∩B2 ∪ ... ∪ ...
其中A1是一个包含A的前i个元素的集合(是的,集合中没有顺序,但让我们假设有一种方式来排序)。如果集合中包含的元素少于完整集合,则返回完整集合。
如果 c 在A(在索引i处)和B(在索引j处),则 c 将在段(而不是索引)max(i,j)中发出。
这样将始终生成一个无限列表(带有无限数量的重复项),无论给定的列表是否有限。唯一的例外是当您给出空列表时,它将永远进行。尽管如此,我们在此确保了交集中的每个元素至少会被发出一次。
使结果有限(如果给定的列表是有限的)
现在我们可以改进我们的定义。首先,我们制作一个更高级版本的take
,takeFinite
(让我们先给出一个直接但不是很有效的定义):
takeFinite :: Int -> [a] -> (Bool,[a])
takeFinite _ [] = (True,[])
takeFinite 0 _ = (False,[])
takeFinite n (x:xs) = let (b,t) = takeFinite (n-1) xs in (b,x:t)
现在我们可以迭代加深,直到
两个列表都达到了末尾:
intersectInfinite :: Eq a => [a] -> [a] -> [a]
intersectInfinite = intersectInfinite' 1
intersectInfinite' :: Eq a => Int -> [a] -> [a] -> [a]
intersectInfinite' n xs ys | fa && fb = intersect xs ys
| fa = intersect ys xs
| fb = intersect xs ys
| otherwise = intersect xfa xfb ++ intersectInfinite' (n+1) xs ys
where (fa,xfa) = takeFinite n xs
(fb,xfb) = takeFinite n ys
现在,由于两个列表都是有限的,该程序将终止,但仍会产生许多重复项。肯定有更好的方法来解决这个问题。