ricecrispie
ricecrispie

Reputation: 13

Multiply squares function

I've been trying to write a function that can take a different number of integer parameters and return the product of the squares of the parameters.

output should be:

multiply_squares(3,2,1)
multiply_squares(2,5)
36
100

So far I've been playing around with this:

def multiply_squares(*num_tuple):
result = []
for num in num_tuple:
    c = num_tuple[num]**2
    result.append(c)

for i in result:
    d *= result[i]
    print(d)

multiply_squares(3,2,1)
multiply_squares(2,5)

I've been getting "tuple index out of range" and haven't been able to solve this properly. Could anyone help me out? And also show me how to turn this into a recursive function?(if possible)

I got a test on Tuesday and I'm trying to understand the logic behind this :)

Any feedback/guidance is appreciated !

Upvotes: 0

Views: 239

Answers (3)

Joe Iddon
Joe Iddon

Reputation: 20434

Why the need for the intermediary list?

def multiply_squares(*num_tuple):
    r = 1
    for n in num_tuple:
        r *= n * n
    return r

and it works:

>>> multiply_squares(3,2,1)
36
>>> multiply_squares(2,5)
100

Recursively:

def multiply_squares(*num_tuple):
    return num_tuple[0] ** 2 * multiply_squares(*num_tuple[1:]) if num_tuple else 1

Just to help with the understanding, I'll just print the inputs to each function call:

>>> multiply_squares(3,2,1)
(3, 2, 1)
(2, 1)
(1,)
()
36

So each guy who gets called knows: its job and its inputs (the numbers). So all it needs to return (either to the initial caller (you) or the guy above him) is what the product of the squares of inputs are.

To do this, the guy can break down his problem by just multiplying the square of the first input to whatever the result of the product of the squares of the other inputs is (through recursively calling himself).

And this will work perfectly... Until, eventually, there are no other inputs. In this case, what does the guy do? Well, this is a special case which will only be instantiated by another person solving the same problem as he is. And the person above him is relying on him to return the product of the squares of that guy's remaining inputs so he can multiply his result to that result. For that reason, he must return 1 otherwise the whole system wouldn't work.

In Python we can test whether the function had no inputs by checking the truthiness of the input tuple. This just so happens to equate to checking whether that data structure has any elements.

I.e.

bool(tuple()) #empty tuple
#False
bool((1,2,3)) #populated tuple
#True

We could just as easily checked whether the length of the tuple was equal (or in this case not equal) to 0.

I.e.

len(tuple()) != 0
#False
len((1,2,3)) != 0
#True

So walking it through:

num_tuple[0] ** 2 * multiply_squares(*num_tuple[1:]) if num_tuple else 1
#     ^           ^                                  ^     ^         ^
# 1st input sqrd  |  by the prod of sqrs of rest     | given inputs  | otherwise 1
#             multiplied                          only if

Why doesn't your code work:

The line:

for num in num_tuple:

iterates over the elements of the num_tuple tuple, not the indicies.

This means that in the first example, num will be 3, then 2 and then 1. Then, instead of indexing the num_tuple with what you thought were the indicies (0, 1 and 2) you are instead indexing with these inputs to the function.

This results in the out of range error as you try to index the tuple with 3 when the length of the tuple is only three elements long (making [2] the last index).

To remedy your solution, you could either change that line to:

for index in range(num_tuple):

and then your indexing will work (if you also replace all previous occurrences of num with index).

Alternatively, a better solution would be to scrap the indexing altogether and just use the elements directly as that is what you want.

That would look something like:

def multiply_squares(*num_tuple):
    result = []
    for num in num_tuple:
        c = num**2
        result.append(c)
    r = 1
    for i in result:
        r *= i
    return r

which now works as expected.

Upvotes: 1

tafaust
tafaust

Reputation: 1518

Please have a look at this two-line solution (because of the assertion) using functools.reduce:

from functools import reduce

a = (3, 2, 1)
b = (2, 5)
c = 2


def multiply_squares(*nums):
    assert nums  # make sure we get input
    return reduce(lambda x, y: x * (y**2), nums, 1)


print(multiply_squares(*a))
print(multiply_squares(*b))
print(multiply_squares(c))
print(multiply_squares())

'''Output:
36
100
4
Traceback (most recent call last):
  File "test.py", line 16, in <module>
    print(multiply_squares())
  File "test.py", line 9, in multiply_squares
    assert nums
AssertionError
'''

which is actually very robust.

Upvotes: 0

jh314
jh314

Reputation: 27812

for num in num_tuple means iterate over num_tuple and set each value to num. num isn't the index of the tuple.

def multiply_squares(*num_tuple):
    result = []
    for num in num_tuple:
        c = num**2
        result.append(c)

    d = 1
    for i in result:
        d *= i
        print(d)

    return d

multiply_squares(3,2,1)
multiply_squares(2,5)

Regarding recursive solution:

def multiply_squares(*num_tuple):
    if not num_tuple: # Base case:
      return 1
    return (num_tuple[0] ** 2) * multiply_squares(*num_tuple[1:])

Upvotes: 2

Related Questions