假设我有两个列表:
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]'
答案 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
但这仍然不是处理它的最佳方法,因为:
(++)
与第一个列表的长度成线性关系,这使其成为二次算法。我们可以通过编写如下代码来解决这些问题:
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]