根据另一个列表过滤haskell列表

时间:2019-02-16 17:55:41

标签: haskell

假设我有两个列表:

l1 = [True, False, True]

l2 = [1, 2, 3]

我希望函数的结果为[1, 3],因为它们映射到另一个列表中的True

这是我想到的:

map2 [] [] res = res
map2 (o:os) (x:xs) res = if o then map2 (os xs (res ++ [x])) else map2 (os xs (res ++ []))

我们可以同时映射2个列表。基本上,如果o为True,那么我想将x添加到列表中,否则就不添加。我认为这应该可以,但是不能。

我明白了无法匹配预期的类型'[a0]-> [a0]-> [Bool]'

2 个答案:

答案 0 :(得分:4)

我想到的最简单的实现方法如下:

map2 :: [Bool] -> [a] -> [a]
map2 bs as = map snd $ filter fst $ zip bs as

也就是说,将两个列表压缩在一起以获得成对的列表,其中第一个是布尔值,指示是否保留它。然后仅过滤第一个广告位中带有True的元素,并使用map snd提取其原始元素。

请注意,以上是一种显式形式,实际上,我可能会简化此操作以删除as参数:

map2 bs = (map snd) . (filter fst) . (zip bs)

答案 1 :(得分:1)

主要问题是您用错误的方式写了括号。如果您用f (g x)之类的方括号括起来,那么Haskell会这样解释,就像C / C ++ / Java语言家族中的一种语言会像f(g(x))那样。

您在这里编写如下表达式:

map2 (os xs (res ++ [x]))

因此,根据语法,os应该是一个函数,xs是它的参数。但是事实并非如此。

我们可以解决以下问题:

map2 :: [Bool] -> [a] -> [a] -> [a]
map2 [] [] res = res
map2 (o:os) (x:xs) res = if o then map2 os xs (res ++ [x]) else map2 os xs res

但这仍然不是处理它的最佳方法,因为:

  1. 这两个列表的长度可能不相同,在这种情况下,该函数将引发错误;和
  2. (++)与第一个列表的长度成线性关系,这使其成为二次算法。

我们可以通过编写如下代码来解决这些问题:

map2 :: [Bool] -> [a] -> [a]
map2 (o:os) (x:xs) | o = x : map2 os xs
                   | otherwise = map2 os xs
map2 _ _ = []

因此,我们在这里不再需要累加器

使用zip

您可以使用zip :: [a] -> [b] -> [(a, b)]同时遍历两个列表并生成这些元素的元组,例如:

Prelude> zip [True, False, True] [1,2,3]
[(True,1),(False,2),(True,3)]

然后我们可以使用它,例如在列表理解中进行过滤,并产生相应的元素,例如:

map2 :: [Bool] -> [a] -> [a]
map2 l1 l2 = [ y | (x, y) <- zip l1 l2, x ]

或者我们可以像@WillNess says一样,将过滤器已经放在模式匹配中,例如:

map2 :: [Bool] -> [a] -> [a]
map2 l1 l2 = [ y | (True, y) <- zip l1 l2]