Mapping a function over two input lists

喜夏-厌秋 提交于 2020-01-14 03:51:09

问题


I have a function that I want to test with several sets of inputs. Let's say the function is

f :: a -> b -> c

Now I have two lists of inputs:

inputA :: [a]
inputB :: [[b]]

For inputA !! i, I want evaluate f $ input !! i for each element of the list at inputB !! i. I know I need several applications of map to do this, but I am having difficulty wrapping my head around a solution.

My most recent attempt is

map f inputA <$> inputB

which gives the following error:

Couldn't match expected type a0 -> b0' with actual type[b1]'
In the return type of a call of map'
Probable cause:
map' is applied to too many arguments
In the first argument of (<$>)', namelymap f inputA'
In the expression: map f inputA inputB

How should I go about solving this problem? I don't necessarily want a complete solution. A push (or even a shove) in a helpful direction would definitely be appreciated.

Additional thoughts:

map f inputA :: [b -> c]

I think this is the right direction. Now I need to map each of the functions over each list of inputs in inputB.

To clarify, I want to map the ith function in map f inputA over the ith list of inputs in inputB to get a result outputC :: [[c]].


回答1:


If I'm understanding you correctly, something like:

mapNested :: (a -> b -> c) -> [a] -> [[b]] -> [[c]]
mapNested f [] _ = []
mapNested f _ [] = []
mapNested f (x:xs) ys = concatMap (map (f x)) ys : mapNested f xs ys

Main> mapNested (+) [1, 2, 3] [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[2,3,4,5,6,7,8,9,10],[3,4,5,6,7,8,9,10,11],[4,5,6,7,8,9,10,11,12]]

If this isn't what you're looking for, could you provide an example input and output?

EDIT

Or is this what you're wanting?

mapNested :: (a -> b -> c) -> [a] -> [[b]] -> [[c]]
mapNested f xs = zipWith map (map f xs)

Main> mapNested (,) [1, 2, 3] [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[(1,1),(1,2),(1,3)],[(2,4),(2,5),(2,6)],[(3,7),(3,8),(3,9)]]



回答2:


You can use zipWith

Prelude> let a = [1,2,3]
Prelude> let b = [[1,2,3],[4,5,6],[7,8,9]]
Prelude> zipWith (\a' bl -> map (+a') bl)  a b
[[2,3,4],[6,7,8],[10,11,12]]



回答3:


everything is easy with list comprehensions:

g f xs yss = [ [f x y | y <- ys] | (x,ys) <- zip xs yss]
           = [ map    (f x)  ys  | (x,ys) <- zip xs yss]
           = [ map     fx    ys  | (fx,ys) <- zip (map f xs) yss]
           = zipWith map (map f xs) yss

           = [ (map . f) x   ys  | (x,ys) <- zip xs yss]
           = zipWith (map . f) xs yss

the last one first shown by nponeccop in the comments, and also hinted at in other answers; we can get it from them by using code transformations

map c $ zip a b    == zipWith c a b
map (c a) b        == (map . c) a b
\a b-> map (c a) b ==  map . c

it seems you've tried to find a pointfree version of it, too:

           = zipWith (map . f) xs yss
           = (zipWith . (map .)) f xs yss

so by eta-reduction g = (zipWith . (map .)) but this might not be easily comprehensible. This is further obfuscated as zipWith <$> (map <$>) and even zipWith <$> ((<$>) <$>).

Or, we can use the ZipList type from Control.Applicative as

           = zipWith (map . f) xs yss
           = getZipList $ pure (map . f)   <*> ZipList xs  <*> ZipList yss
           = getZipList $      (map . f)   <$> ZipList xs  <*> ZipList yss
           = getZipList $       map <$> (f <$> ZipList xs) <*> ZipList yss



回答4:


If I understand you correctly, this is what you need:

Prelude> let f x y = x + y
Prelude> let xs = [1, 2, 3, 4, 5]
Prelude> let ys = [[1, 2], [3, 4, 5], [6, 7], [8], [9, 10]]
Prelude> map (\(x, ys) -> map (f x) ys) $ zip xs ys
[[2,3],[5,6,7],[9,10],[12],[14,15]]
Prelude> 

i.e.

fancyZipMap :: (a -> b -> c) -> [a] -> [[b]] -> [[c]]
fancyZipMap f xs yys = map (\(x, ys) -> map (f x) ys) $ zip xs yys


来源:https://stackoverflow.com/questions/19194396/mapping-a-function-over-two-input-lists

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!