2

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]].

2
  • 3
    In case you haven't seen it yet, you might also be interested in QuickCheck. Commented Oct 5, 2013 at 7:46
  • @DanielWagner Thanks for the link. I have read about QuickCheck very briefly and it is on my List of Things to Learn About Haskell. Commented Oct 5, 2013 at 18:03

4 Answers 4

6

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]]
Sign up to request clarification or add additional context in comments.

Comments

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   where  c' (a,b) = 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 $ liftA2 (map . f)    (ZipList xs)    (ZipList yss)
           = getZipList $ pure   (map . f) <*> ZipList xs  <*> ZipList yss
           = getZipList $        (map . f) <$> ZipList xs  <*> ZipList yss
           = getZipList $       map <$> (f <$> ZipList xs) <*> ZipList yss

Comments

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 Comments

This is almost what I'm going for. However, using your example, I want to add 1 to the elements of the first list, 2 to the second list, and 3 to the third list rather than adding each number to all of elements of every list.
You get the check mark for being the first to answer. After seeing your use of zipWith, it seems quite obvious (especially since I am already using zipWith in several other related contexts).
1

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

1 Comment

A shorter version: fancyZipMap f = zipWith $ map . f

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.