我从可计算性理论知道可以取两个无限列表的交集,但我找不到在Haskell中表达它的方法。
传统方法一旦第二个列表无限就会失败,因为您花费所有时间在第一个列表中检查它是否存在非匹配元素。
示例:
let ones = 1 : ones -- an unending list of 1s
intersect [0,1] ones
这永远不会产生1
,因为它永远不会停止检查元素ones
的{{1}}。
成功的方法需要确保在有限的时间内访问每个列表的每个元素。
这可能是通过迭代两个列表,并花费大致相等的时间检查每个列表中所有先前访问过的元素。
如果可能的话,我也希望能够忽略列表中的重复项,因为这有时是必要的,但这不是必需的。
答案 0 :(得分:9)
使用universe package's Cartesian product operator我们可以写这个单行:
import Data.Universe.Helpers
isect :: Eq a => [a] -> [a] -> [a]
xs `isect` ys = [x | (x, y) <- xs +*+ ys, x == y]
在ghci中尝试:
> take 10 $ [0,2..] `isect` [0,3..]
[0,6,12,18,24,30,36,42,48,54]
如果输入列表没有任何副本,则此实现不会产生任何重复;但如果他们这样做,您可以在致电isect
之前或之后使用您最喜爱的复印机。例如,使用nub
,您可以编写
> nub ([0,1] `isect` repeat 1)
[1
然后加热你的计算机非常好,因为如果它看起来足够深,它就永远无法确定某个地方的第二个列表中可能没有0
。
这种方法比David Fletcher快得多,产生的副本少得多,并且比Willem Van Onsem更快地生成新值,并且不假设列表像自由式一样排序(但因此在这些列表上比自由式更慢) )。
答案 1 :(得分:6)
一个想法可能是使用递增边界。让我们首先放松一下这个问题:允许产生重复的值。在这种情况下,您可以使用:
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= A 1 ∩B 1 ∪A 2 ∩B 2 ∪... ∪...
带有 1 的是一个包含 A 的第一个元素的集合(是的,集合中没有订单,但让&# 39; s说有某种程度的命令)。如果集合包含 less 元素,则返回完整集。
如果 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
现在这将终止,因为两个列表都是有限的,但仍会产生大量重复。肯定有更多方法可以解决这个问题(如果我有更多时间会更新)。
答案 2 :(得分:5)
这是一种方式。对于每个x
,我们列出了具有的maybes列表
Just x
仅x
出现ys
的地方isect :: Eq a => [a] -> [a] -> [a]
isect xs ys = (catMaybes . foldr interleave [] . map matches) xs
where
matches x = [if x == y then Just x else Nothing | y <- ys]
interleave :: [a] -> [a] -> [a]
interleave [] ys = ys
interleave (x:xs) ys = x : interleave ys xs
。然后我们交错了所有
这些清单。
> take 10 (isect [0..] [0,2..])
[0,2,4,6,8,10,12,14,16,18]
也许可以使用某种更公平的交错来改进它 - 在下面的例子中它已经很慢了,因为(我认为) 它正在做一些指数的工作。
<input type="text" name="vat" id="vat1" placeholder="Please Enter 10 digits number">
答案 3 :(得分:4)
如果列表中的元素已经订购,那么您可以轻松完成。
intersectOrd :: Ord a => [a] -> [a] -> [a]
intersectOrd [] _ = []
intersectOrd _ [] = []
intersectOrd (x:xs) (y:ys) = case x `compare` y of
EQ -> x : intersectOrd xs ys
LT -> intersectOrd xs (y:ys)
GT -> intersectOrd (x:xs) ys
答案 4 :(得分:2)
这是另一种选择,利用Control.Monad.WeightedSearch
import Control.Monad (guard)
import Control.Applicative
import qualified Control.Monad.WeightedSearch as W
我们首先定义在列表中挖掘的成本。进入尾部需要多花费1个单位。这将确保两个无限列表之间的公平调度。
eachW :: [a] -> W.T Int a
eachW = foldr (\x w -> pure x <|> W.weight 1 w) empty
然后,我们只是忽略无限列表。
intersection :: [Int] -> [Int] -> [Int]
intersection xs ys = W.toList $ do
x <- eachW xs
y <- eachW ys
guard (x==y)
return y
使用MonadComprehensions
时更好:
intersection2 :: [Int] -> [Int] -> [Int]
intersection2 xs ys = W.toList [ y | x <- eachW xs, y <- eachW ys, x==y ]
答案 5 :(得分:0)
我最终使用了以下实现; David Fletcher略微修改了答案:
isect :: Eq a => [a] -> [a] -> [a]
isect [] = const [] -- don't bother testing against an empty list
isect xs = catMaybes . diagonal . map matches
where matches y = [if x == y then Just x else Nothing | x <- xs]
这可以通过nub来增加,以过滤掉重复项:
isectUniq :: Eq a => [a] -> [a] -> [a]
isectUniq xs = nub . isect xs
行isect xs = catMaybes . diagonal . map matches
(map matches) ys
计算xs
和ys
元素之间的比较列表,其中列表索引分别指定ys
和xs
中的索引:ie (map matches) ys !! 3 !! 0
表示ys !! 3
与xs !! 0
的比较,如果这些值不同,则为Nothing
。如果这些值相同,则该值为Just
。
diagonals
获取列表列表并返回列表列表,其中第n个输出列表包含前n个列表中的元素。另一种概念化方法是(diagonals . map matches) ys !! n
包含元素之间的比较,其中xs
和ys
的索引总和为n
。
diagonal
只是diagonals
(diagonal = concat diagonals
)
因此(diagonal . map matches) ys
是xs
和ys
元素之间的比较列表,其中元素大致按ys
元素索引的总和排序和xs
被比较;这意味着将早期元素与后面的元素进行比较,优先级与中间元素相互比较。
(catMaybes . diagonal . map matches) ys
是仅包含两个列表中元素的列表,其中元素大致按要比较的两个元素的索引之和进行排序。
注意强>
(diagonal . map (catMaybes . matches)) ys
不工作:catMaybes . matches
仅在找到匹配时产生,而不是在不匹配时产生Nothing
,因此交错不会分配工作。
相比之下,在所选择的解决方案中,Nothing
和Just
值之间的交错diagonal
意味着程序将其注意力分散在“搜索”多个不同元素之间,而不是等待让一个人成功;而如果在交错之前移除Nothing
值,则程序可能花费太多时间等待对于给定元素成功的无效“搜索”。
因此,我们会遇到与原始问题相同的问题:当一个元素与另一个列表中的任何元素都不匹配时,程序将挂起;而所选择的解决方案只会挂起而没有找到任何列表中任何元素的匹配。