warty
warty

Reputation: 49

Apply a function to a list of items

I have defined a function f that returns the price of an item x that is stored inside a list of prices with type [(String,String,Int)]

a = String
x = String
price = Int
pricesList = [(a,x,price)]

f a x ((a1, x1, price):ys) | a == a1 && x == x1 = price
                           | otherwise = f a x ys

I have to apply this function to a list of items, but I'm stuck. Is this possible using map? I couldn't figure it out.

(The only function that can use recursion is f)

Edit. some examples to clarify a bit

pricesList = [("apple","ipod",100),("apple","iphone",200),("samsung","galaxy",200)]
moneySpent = [("harry",1985,"apple",["iphone","ipod"]),("george",1983,"samsung",["galaxy"])]

*Main> f "apple" "iphone" pricesList
200

I need to know how much money a person spent by defining a new function, let's say spentBy (and using f inside of it)

*Main> spentBy "harry"
300

What I have done so far:

itsThePerson name (n,_,_,_) = name == n

infoFrom name = (head . filter (itsThePerson name)) moneySpent

brand (_,_,b,_) = b
product (_,_,_,p) = p

brandPerson = brand . infoFrom
productPerson = product . infoFrom

Is it possible to use map with function f to know the sum of the prices of the products that a person bought?

(function f would be function itemPrice)

spentBy name = sum (map (itemPrice (brandPerson name) xxx pricesList) (productPerson name)

Am I thinking it in the right direction?

Upvotes: 0

Views: 214

Answers (4)

warty
warty

Reputation: 49

I could solve my problem:

f a x ((a1, x1, price):ys) | a == a1 && x == x1 = price
                           | otherwise = f a x ys

Used a new function f2

f2 a x = f a x pricesList

Then

pricesList = [("apple","ipod",100),("apple","iphone",200),("samsung","galaxy",200)]
moneySpent = [("harry",1985,"apple",["iphone","ipod"]),("george",1983,"samsung",["galaxy"])]


spentBy name = sum (map (f2 (brandPerson name)) (productPerson name))

itsThePerson name (n,_,_,_) = name == n

infoFrom name = (head . filter (itsThePerson name)) moneySpent

brand (_,_,b,_) = b
product (_,_,_,p) = p

brandPerson = brand . infoFrom
productPerson = product . infoFrom

Upvotes: 0

iamnat
iamnat

Reputation: 4166

Modified answer: The overall problem you're trying to solve,

  • Given a function f that takes 'itemId', 'itemName' and 'priceList' and returns a price.
  • You want a function that uses f to return a list of prices from a list of [(itemId, itemName)]

In terms of type signatures, given:

f :: String -> String -> [(String, String, Int)] -> Int

and you want:

myLookup :: (String -> String -> [(String, String, Int)] -> Int) -> 
                [(String, String, Int)] -> [(String, String)] -> [Int]

where the first argument is the lookup function f, a priceList and an itemList, and the output is a list of prices.

myLookup f priceList itemList = map (\(itemId, itemName) -> 
                                          f itemId itemName priceList)
                                    itemList

The second argument to map is a lambda that extracts the two attributes from the itemList and looks up the values using f.

Upvotes: 1

mhwombat
mhwombat

Reputation: 8136

If you provided a type signature for f, it would be clearer what you were trying to do. I'm going to assume that the third argument of f is a list of (item #, description, price). An example would be:

priceList = [(1,"item 1", 1), (2,"item 2", 22), (3, "item 3", 333), (4, "item 4", 4444)]

Then the list we plan to map over must look something like this:

list = [(1,"item 1"), (3,"item 3"), (4, "item 4")]

We can map over list like so:

map (\(itemId, desc) -> f itemId desc priceList) list

You could instead write f to take two parameters, combining the first two into a tuple f (a,x) ((a1, x1, price):ys). The all you need is map f list.

Finally, you haven't handled the case where the price isn't in the list. You could do something like this:

 f _ _ _ = error "item not found"

Upvotes: 2

Benesh
Benesh

Reputation: 3428

Let's say you've got a three-argument function:

f :: a -> b -> c -> d

and a list of c values - list :: [b]. Let's say we want to apply f to each item in the list, with fixed first and second arguments. So we can do something like that:

map (f x y) list

where x::a and y::b.

The only difference from your case is the order of arguments. For this kind of issues, use flip:

flip :: (a -> b -> c) -> (b -> a -> c)

--original function
f :: a -> b -> c -> d

--partial application
f x :: b -> c -> d

-- flip
flip (f x) :: c -> b -> d

--partial application again
(flip (f x) y) :: b -> d

map (flip (f x) y) list :: [d]

In your case it'll probably result in something like map (flip (f a) prices) list.

Upvotes: 3

Related Questions