1

I have to calculate the total value f my stock based on the price per unit and number of units. I have the following code in Python:

prices = { "banana" : 4, "apple" : 2, "orange" : 1.5, "pear" : 3, } stock = { "banana" : 6, "apple" : 0, "orange" : 32, "pear" : 15, } for key in prices: print key print "price: %s" % prices[key] print "stock: %s" % stock[key] total = 0 for key in prices: total = total + prices[key]*stock[key] print total 

And I've tried to convert it to a more functional program by replacing the last block with this:

def total(x): if len(x) == 1: return prices[prices.keys()[0]]*stock[prices.keys()[0]] else: return prices[prices.keys()[0]]*stock[prices.keys()[0]] + total(x[1:]) print total(prices) 

The above code gets this error:

Traceback (most recent call last): File "python", line 30, in <module> File "python", line 28, in total TypeError: unhashable type 

Can someone please correct my code to a more functional programming version?

5 Answers 5

1

First, let's look at the imperative loop:

total = 0 for key in prices: total = total + prices[key]*stock[key] print total 

Examining the imperative loop, the two things that are changing every iteration are total, which is fine by itself, and key, which originates from prices.keys(). So, we'll need something from those.

Let's try rewriting the imperative loop in natural language. I'll pick English. For each key in prices [or perhaps that should be prices.keys()], increase the total by prices[key]*stock[key].

Since we cannot mutate total, let's rewrite the statement:

For each key in prices.keys(), increase the running total by prices[key]*stock[key].

And, since the prices.keys() are names of fruits, let's write it again:

For each key in fruitNames, increase the running total by prices[key]*stock[key].

Now, here is a mental jump I can't quite explain. The hints were that total and key were changing through each iteration of the loop. We can ignore total for now (because I am not going to confuse this further with tail-recursion-optimization). For functional style, the key becomes the entire list of keys, fruitNames.

def totalRecur(fruitNames): 

Now, let's think about the base case. What if prices (and stock) were empty? Well, the total would zero:

 if len(fruitNames) == 0: return 0 

That looks just fine. Now, what if there is only one item at position zero?

 key = fruitNames[0] return prices[key] * stock[key] 

Because we know that totalRecur([]) == 0, we can instead say

 return prices[key] * stock[key] + totalRecur([]) 

And, because the list only has one item, we know that fruitNames[1:] is the empty list:

 return prices[key] * stock[key] + totalRecur(fruitNames[1:]) 

That should give you enough information to write a good definition of totalRecur.

Sign up to request clarification or add additional context in comments.

7 Comments

Recursion is generally to be avoided in Python. Functional != recursion.
?? Stop being so dramatic. I'm simply stating that if you are going to give advice on how to write code in a particular language, you should not encourage the use of constructs that are generally result in inferior performance. Python is not built for recursion, there is a lot of overhead associated with allocating a new stack-frame, and there is no tail-call optimization. There is also a recursion limit. There are some cases, like walking down a tree you know isn't very deep, where the simplicity of the recursive implementation could outweigh a non-recursive implementation.
Oops! I apologize for my vague tone. I wasn't trying to be dramatic at all. I was agreeing with you. I've always thought functional implied heavy use of recursion. Then I thought about what you said, and remembered some of my troubles using lots of tail-recursion in Python 3, which still emits an error when the maximum recursion depth is exceeded.
No, no. The apologies are mine. I misread your comment as saying "StackOverflow [the site] sucks".
I got the error message wrong, anyway. It should read RuntimeError: maximum recursion depth exceeded.
|
1

Using generator expressions or list/set/dictionary comprehensions is very functional:

In [1]: prices = { ...: "banana" : 4, ...: "apple" : 2, ...: "orange" : 1.5, ...: "pear" : 3, ...: } ...: stock = { ...: "banana" : 6, ...: "apple" : 0, ...: "orange" : 32, ...: "pear" : 15, ...: } ...: In [2]: total_value = sum(stock[k]*prices[k] for k in stock) In [3]: total_value Out[3]: 117.0 In [4]: 

1 Comment

@GuillaumeJacquenot I disagree with the edit. Pasting the output of an interactive interpreter session is common on StackOverflow. Removing the In/Out lines actually makes it non-sensical, because the last line only works like that in an interactive session
1

If by functional programming, you mean by using higher order functions and lambdas:

sum(map(lambda k, v: v * stock[k], prices.items())) 

you get an error because the expression x[1:] is a dictionary, not a key

Comments

1

You can use a dict comprehension to return the value you want without side effects, avoiding state and mutation too (See Functional Programming):

def total(prices, stock): return sum([p * stock[k] for k, p in prices.items() if k in stock]) >>> total(prices, stock) >>> 117.0 

This answer provided the inspiration.

Comments

-2
def total(stock): return sum([v*prices[k] for k,v in stock.iteritems()]) #.iteritems() is a built in method for dicts. it returns key, value pairs #same as i, dict[i] 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.