Haskell: map function with tuples

I need to write a Haskell program that does the following:

Main> dotProduct [(1,3),(2,5),(3,3)] 2 [(2,3),(4,5),(6,3)] 

I need to do this with and without the map function. I already did this without map , but I do not know how to do this using map .

My dotProduct without map function:

 dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)] dotProduct [] _ = [] dotProduct [(x,y)] z = [(x*z,y)] dotProduct ((x,y):xys) z = (x*z,y):dotProduct (xys) z 

So I really need help with the map version.

+4
source share
3 answers

EEVIAC has already sent a response, so I’ll just explain how to do it myself. As you probably know, map has a signature like (a -> b) -> [a] -> [b] . Now dotProduct is of type [(Float, Integer)] -> Float -> [(Float, Integer)] , and you will call map somewhere there, so it should look something like this:

 dotProduct theList z = map (??? z) theList 

where ??? - a function of type Float -> (Float, Integer) -> (Float, Integer) - this follows directly from the signature of the map type and from the fact that we pass z function that we must do, simply because there is no other place for its use.

The thing with map functions and higher order in general is that you have to keep in mind what a higher order function does and β€œjust” provide it with the correct function. Since map applies this function to all elements of the list, your function should work with only one element, and you can forget everything about the list - map will take care of this.

+2
source

Instead of starting with trying to pick up a map in some way, think about how to simplify and generalize the current function. Starting from this:

 dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)] dotProduct [] _ = [] dotProduct [(x,y)] z = [(x*z,y)] dotProduct ((x,y):xys) z = (x*z,y):dotProduct (xys) z 

First, we rewrite the second case using the constructor (:) :

 dotProduct ((x,y):[]) z = (x*z,y):[] 

Expand [] as a result using the first case:

 dotProduct ((x,y):[]) z = (x*z,y):dotProduct [] z 

Comparing this with the third case, we see that they are identical, except that it is specialized for xys [] . So, we can simply completely eliminate the second case:

 dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)] dotProduct [] _ = [] dotProduct ((x,y):xys) z = (x*z,y):dotProduct (xys) z 

Next, generalizing the function. First we rename it and let dotProduct name it:

 generalized :: [(Float, Integer)] -> Float -> [(Float, Integer)] generalized [] _ = [] generalized ((x,y):xys) z = (x*z,y):generalized (xys) z dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)] dotProduct xs z = generalized xs z 

First, we parameterize it with an operation specializing in multiplication for dotProduct :

 generalized :: (Float -> Float -> Float) -> [(Float, Integer)] -> Float -> [(Float, Integer)] generalized _ [] _ = [] generalized f ((x,y):xys) z = (fxz,y):generalized f (xys) z dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)] dotProduct xs z = generalized (*) xs z 

Further, we can observe two things: generalized no longer dependent on arithmetic, so it can work on any type; and the only time z used as the second argument of f , so we can combine them into one argument of the function:

 generalized :: (a -> b) -> [(a, c)] -> [(b, c)] generalized _ [] = [] generalized f ((x,y):xys) = (fx, y):generalized f (xys) dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)] dotProduct xs z = generalized (* z) xs 

Now note that f used only for the first element of the tuple. This sounds useful, so we will extract this as a separate function:

 generalized :: (a -> b) -> [(a, c)] -> [(b, c)] generalized _ [] = [] generalized f (xy:xys) = onFirst f xy:generalized f (xys) onFirst :: (a -> b) -> (a, c) -> (b, c) onFirst f (x, y) = (fx, y) dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)] dotProduct xs z = generalized (* z) xs 

Now we again notice that in generalized f used only with onFirst , so we again combine them into one function argument:

 generalized :: ((a, c) -> (b, c)) -> [(a, c)] -> [(b, c)] generalized _ [] = [] generalized f (xy:xys) = f xy:generalized f (xys) dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)] dotProduct xs z = generalized (onFirst (* z)) xs 

And once again, generalized no longer dependent on a list containing tuples, so we allow it to work with any type:

 generalized :: (a -> b) -> [a] -> [b] generalized _ [] = [] generalized f (x:xs) = fx : generalized f xs 

Now compare the code for generalized with this:

 map :: (a -> b) -> [a] -> [b] map _ [] = [] map f (x:xs) = fx : map f xs 

It also turns out that there is also a slightly more general version of onFirst , so we will replace this and generalized with their standard library equivalents:

 import Control.Arrow (first) dotProduct :: [(Float, Integer)] -> Float -> [(Float, Integer)] dotProduct xs z = map (first (* z)) xs 
+13
source
 dotProduct xs z = map (\(x,y) -> (x*z,y)) xs 

The (\(x,y) -> (x*z,y)) is a function that takes a pair and returns a new pair that looks like the old one, except that its first component is multiplied by z . The map function takes a function and applies it to each element in the list. Therefore, if we pass the function (\(x,y) -> (x*z,y)) to map , it will apply this function to each element in xs .

Although you are sure that your first is correct? A point product operation is usually defined so that it takes two vectors, multiplies the corresponding component, and then sums it all together. Like this:

 dotProduct xs ys = sum $ zipWith (*) xs ys 
+3
source

All Articles