i got this task today from school to make a function Sum(list):
i researched and found out few ways how to make it work with list that consist of int, or list of list of integers, however my function should work also with other types like string, or tuples. Here are some examples of what it should do.
Sum([1,2,3]) returns 6; Sum([2,[1.5,3],0.5]) returns 7.0;
Sum([[['p'],'yt'],['h',['on']]]) returns 'python'
Sum([[(1,2)],(3,),[(4,5)]]) returns (1, 2, 3, 4, 5);
And on top of that all it should be also able to compute if argument in the function Sum(X) is NOT list. Example:
Sum(5*5) returns 25;
This is what i am working with so far, works fine for first 2 cases, however i cant find a way to modify it to work for other examples. Personaly ive been trying to use isinstance and exceptions
def list_sum(L):
total = 0
for i in L:
if isinstance(i, list):
total += list_sum(i)
else:
total += i
return total
Many thanks for any kind of help
Use the first non-list value as the initial value for the sum. Let None signify that we did not yet see such value:
def list_sum(L):
if not isinstance(L, list):
return L
total = None
for i in L:
i = list_sum(i)
if total is None:
total = i
else:
total += i
return total
Also, in this case, using isinstance is the sanest thing to do.
For the really robust case that handles empty lists correctly, make use of a flattening generator:
def list_sum(l):
def flatten(l):
for el in l:
if isinstance(el, list):
for sub in flatten(el):
yield sub
else:
yield el
if isinstance(l, list):
return sum(flatten(l))
return l
Note that the builtin sum function works in that case.
Here is a one-liner for doing this with just integers, for future reference:
from itertools import chain as flatten
def nested_sum(l):
return sum(flatten.from_iterable(l))
itertools.chain is great for flattening nested iterables.
Related
A little background: I've been trying to code a "Sieve of Eratosthenes" algorithm. At the request of some fine (and very patient) programmers on the StackOverflow Python chatroom, I did some reading on the enumerate() function and found a way to incorporate it into my code (yes, I'm very much a novice here). So far, my (workable; returns expected response) code looks like this:
def SieveErat(n):
numbers = [False]*2+[True]*(n-1)
for index, prime_candidate in enumerate(numbers):
if prime_candidate == True:
yield index
for x in xrange(index*index, n, index):
numbers[x] = False
primes = []
for x in SieveErat(150000):
primes.append(x)
print primes[10002]
Needless to say, the enumerate() function makes coding this much, much less awkward than whatever nested loops I had before. However, I fear that I'm not understanding something about enumerate(), because when I tried to shorten this code by including the appending into the function, I kept getting errors- namely,
File "SievErat.py", line 13
return numbers
SyntaxError: 'return' with argument inside generator
I've also tried appending all the True elements inside the list numbers to a initialized list primes, but found no luck.
Any hints or advice would be very much welcomed.
This has nothing to do with enumerate you are trying to return something in a generator which before python 3.3 is illegal, and from 3.3+ it means something entirely different.
I'd recommend you leave your function a generator if you may use it without needing a list back, and if you want list results then just call list() on the return value:
primes = list(SieveErat(150000)) #this replaces loop with .append
But to understand what went wrong, if your function still has the yield statement in it then it must return a generator object, if you don't want it to return a generator object then remove the yield statement all together:
def SieveErat(n):
numbers = [False]*2+[True]*(n-1)
for index, prime_candidate in enumerate(numbers):
if prime_candidate == True:
#yield index #no yield statement if you want to return the numbers list
for x in xrange(index*index, n, index):
numbers[x] = False
return numbers #return at end
However this would then return a list of True and False instead of the numbers that are True, you can instead hold a separate list with all the prime numbers and .append to it everytime you would yield something:
def SieveErat(n):
numbers = [False]*2+[True]*(n-1)
result = [] #start with no results
for index, prime_candidate in enumerate(numbers):
if prime_candidate == True:
results.append(index) #instead of yield index
for x in xrange(index*index, n, index):
numbers[x] = False
return results
But this feels like a step backwards from a generator, personally I'd just keep what you have posted and cast the result to a list.
I'm currently working with a recursive function in Python, and I've run into a wall. As titled, the problem is to return the maximum depth of an arbitrarily nested list.
Here is what I have so far:
def depthCount(lst):
'takes an arbitrarily nested list as a parameter and returns the maximum depth to which the list has nested sub-lists.'
var = 0
if len(lst) > 0:
if type(lst[0]) == list:
var += 1
depthCount(lst[1:])
else:
depthCount(lst[1:])
else:
return var
I feel that the problem is with my recursive calls (this may be obvious). It will indeed return var when the list has reached the end, but when I have a nonempty list, things go awry. Nothing is returned at all.
Am I slicing wrong? Should I be doing something before the slice in the recursive call?
The problem may also be with my base case.
If they are just nested lists, e.g., [[[], []], [], [[]]], here's a nice solution:
def depthCount(lst):
return 1 + max(map(depthCount, lst), default=0)
Here's a slight variation you could use if you don't use Python 3.4, where the default argument was introduced:
def depthCount(lst):
return len(lst) and 1 + max(map(depthCount, lst))
They also differ by how they count. The first considers the empty list to be depth 1, the second to be depth 0. The first one is easy to adapt, though, just make the default -1.
If they're not just nested lists, e.g., [[[1], 'a', [-5.5]], [(6,3)], [['hi']]]), here are adaptions to that:
def depthCount(x):
return 1 + max(map(depthCount, x)) if x and isinstance(x, list) else 0
def depthCount(x):
return int(isinstance(x, list)) and len(x) and 1 + max(map(depthCount, x))
Make sure you understand how the latter one works. If you don't know it yet, it'll teach you how and works in Python :-)
Taking the "purely recursive" challenge:
def depthCount(x, depth=0):
if not x or not isinstance(x, list):
return depth
return max(depthCount(x[0], depth+1),
depthCount(x[1:], depth))
Granted, the extra argument is slightly ugly, but I think it's ok.
It will indeed return var when the list has reached the end, but when I have a nonempty list, things go awry. Nothing is returned at all.
That's because you have no return statement, except in the else base case for an empty list. And if you fall off the end of the function without hitting a return, that means the function returns None.
But you have another problem on top of that. You're starting var = 0, then possibly doing var += 1… but you're not passing that down into the recursive calls, or using any result from the recursive calls. So the recursive calls have no useful effect at all.
What you probably meant is something like this:
def depthCount(lst):
'takes an arbitrarily nested list as a parameter and returns the maximum depth to which the list has nested sub-lists.'
if len(lst) > 0:
if type(lst[0]) == list:
return 1 + depthCount(lst[1:])
else:
return depthCount(lst[1:])
else:
return 0
But this still isn't actually right. The depth count of a list is 1 more than the depth count of its deepest element. Just checking its second element won't do you any good; you need to check all of them. So, what you really want is something like this:
def depthCount(lst):
'takes an arbitrarily nested list as a parameter and returns the maximum depth to which the list has nested sub-lists.'
if isinstance(lst, list):
return 1 + max(depthCount(x) for x in lst)
else:
return 0
If you want to replace that iterative for x in lst with a second layer of recursion, of course you can, but I can't see any good reason to do so; it just makes the code more complicated for no reason. For example:
def max_child_count(lst):
if lst:
return max(depth_count(lst[0]), max_child_count(lst[1:]))
else:
return 0
def depth_count(lst):
if isinstance(lst, list):
return 1 + max_child_count(lst)
else:
return 0
This may still not be right. It definitely does the right thing for, e.g., [1, [2,3], [4, [5]]]. But what should it do for, say, []? I can't tell from your question. If it should return 0 or 1, you'll obviously need to change the if appropriately. If that's illegal input, then it's already doing the right thing. (And that should also answer the question of what it should do for, e.g., [[[], []], [], [[]]], but make sure you think through that case as well.)
So, essentially, the data structure that you're referring to is a k-ary tree, also known as n-ary tree, with arbitrary branching. Here's the code for determining the max. depth of a n-ary tree with arbitrary branching.
def maxdepth(tree):
if isleaf(tree):
return 1
maximum = 0
for child in children(tree):
depth = maxdepth(child)
if depth > maximum:
maximum = depth
return maximum + 1
You can see the code in action with different test inputs here.
I need to sum objects (strings, ints, etc.) in one function (don't create other function, it can be done within one). It should work this way: When given my_sum([[['s'],'ta'],['c',['k']]]), it should return 'stack'.
I came up with this:
def my_sum(array):
if not array: #empty array
return None
else:
for item in array:
if type(item) == list:
my_sum(item)
else:
print(item)
It of course is not doing what it should be, I was just playing around with it trying to come up with something. This code above returns this:
s
ta
c
k
I think I am not that far from result as I have what I need, but here is the problem how can I sum up those items ? I can't write result = '' anywhere in the function and then return it, because it would be deleting every time there would be recursion call. Also I don't want global variables (if anyone would think of that). Maybe I am just being stupid and can't see that it is one simple thing, pardon me if it is so.
Thank you for every answer!
The common accumulating pattern is:
result = <init value>
for item in argument:
result = result <operator> item
return result
(this can be written more concisely, but that's not the point for now).
Applied to your problem:
def my_sum(items):
result = ''
for item in items:
if type(item) == list:
result += my_sum(item)
else:
result += item
return result
Note that type(x) == y is frowned upon in Python, isinstance is considered better style.
Homework: extend the function so that it works for these arguments too:
print my_sum([[['s'],'ta'],('c',('k')), {('over'), ('flow')}])
I'm trying to solve the following exercise from Google's Python class.
E. Given two lists sorted in increasing order, create and return a merged list of all the elements in sorted order. You may modify the passed in lists. Ideally, the solution should work in "linear" time, making a single pass of both lists.
I'm using the following Scheme approach (I wish I had car, cdr and cons!).
def helper(list1, list2, result):
if list1 == None:
return result + list2
elif list2 == None:
return result + list1
elif list1[0] < list2[0]:
return helper(list1[1:], list2, result.insert(0, list1[0]))
else:
return helper(list1, list2[1:], result.insert(0, list2[0]))
def linear_merge(list1, list2):
helper(list1, list2, [])
The error I get is, I can't seem to insert an element into result when result is []:
AttributeError: 'NoneType' object has no attribute 'insert'
But that works fine in the console:
>>> b = []
[]
>>> b.insert(0, 4)
>>> b
[4]
I'm brand new to Python, so I have two questions:
What am I missing about None vs. [], and how do I get this code to work?
If Python wasn't meant for a Scheme/Lisp approach, what's the "Python way" of solving this? This is less important to me, since I can just check the solutions.
Thanks!
list.insert returns None, not the modified list.
This requires helper to be changed to read
def helper(list1, list2, result):
if not list1:
return result + list2
elif not list2:
return result + list1
elif list1[0] < list2[0]:
return helper(list1[1:], list2, result + [list1[0]])
else:
return helper(list1, list2[1:], result + [list2[0]])
Note the changes to the two base cases. None and the empty list [] are not the same thing. The pythonic way of testing if a list is empty is to treat the list as a Boolean value: empty lists are False, all others are True.
And as the others have noticed before me, you need to explicitly return the return value of helper in linear_merge.
The first problem is that [] and None are not equal.
This causes two problems for you.
First, your test for the recursive base case isn't working. If you're trying to test for a list being empty, there are a number of ways to do it:
if not list1:
if list1 == []:
if len(list1) == 0:
But comparing it to None is not one of those ways.
The first is generally considered the most Pythonic (and explicitly encouraged by the PEP 8 style guide).
Second, you're apparently explicitly calling this function with None as an argument somewhere in code you haven't shown us. Don't do that. Call it with [] when you mean [].
And the other problem is that mutable methods like list.insert don't return the mutated object, they return None. So, instead of this:
return helper(list1[1:], list2, result.insert(0, list1[0]))
… you need to either do this:
result.insert(0, list1[0])
return helper(list1[1:], list2, result)
… or use a non-mutating expression instead:
return helper(list1[1:], list2, [list1[0]] + result)
And then, your linear_merge doesn't return anything, so its value will be None. Change it to:
return helper(list1, list2, [])
result.insert does not return a new list; it modifies an existing result in place. Thus you wind up passing None as the third argument to your nested helper() calls, because that's what result.insert returns - None.
Also note:
def linear_merge(list1, list2):
helper(list1, list2, [])
Since you don't return anything from linear_merge, you're always going to get None as the result.
Since you asked for a Pythonic solution, as well as how to fix your attempt, I'll write you one as a separate answer.
I'd do this by using iterators. At each step, you yielding the lower value and pulling the next one. The same way you'd do it in Haskell.* This actually is linear time, and it's lazy as well. Using more-itertools:
def itermerge(i1, i2):
i1, i2 = iter(i1), more_itertools.peekable(iter(i2))
for v1 in i1:
while i2 and i2.peek() < v1:
yield next(i2)
yield v1
yield from i2
If you need Python 2.x or 3.2 compatibility, just change the yield from i2 with for v2 in i2: yield v2.
If it's not clear how this works, here's the most explicit version, to show exactly what's happening at each step:
def itermerge(i1, i2):
i1, i2 = iter(i1), iter(i2)
sentinel = object()
v1, v2 = sentinel, sentinel
while True:
if v1 is sentinel:
try:
v1 = next(i1)
except StopIteration:
yield from i2
return
if v2 is sentinel:
try:
v2 = next(i2)
except StopIteration:
yield from i1
return
if v1 < v2:
yield v1
v1 = sentinel
else:
yield v2
v2 = sentinel
If you need a function that returns a list, that's easy:
def linear_merge(l1, l2):
return list(itermerge(l1, l2))
* This is a bit of a joke. While you can write pretty much any of the immutable versions of this algorithm in Haskell, the one that's most like this solution is probably not the one you'd write.
I came across the following in How to Think Like a Computer Scientist (here):
def recursive_sum(nested_num_list):
sum = 0
for element in nested_num_list:
if type(element) == type([]):
sum = sum + recursive_sum(element)
else:
sum = sum + element
return sum
I was shocked by the use of type(element) == type([]). Not only is it bad practice, but this function won't work for any other sequence types. Polymorphism is the typical way of avoiding type comparisons, but can't be used here. How could one avoid the type comparison in such a case? I considered:
def recursive_sum(nested_sum_list):
sum = 0
for element in nested_num_list:
try:
sum += element
except TypeError:
sum += recursive_sum(element)
return sum
which makes the function applicable to other sequences, but is still kinda gross. Thanks!
You can check if an element is a sequence by using isinstance(element, collections.Sequence).
"sum" functions takes an iterable, so I would check the element implements the __iter__ method or not,using "hasattr" builtin function.
Like this:
def recursive_sum(nested_num_list):
sum = 0
for element in nested_num_list:
if hasattr(element, '__iter__'):
sum = sum + recursive_sum(element)
else:
sum = sum + element
return sum
For the flattening of an aribtrarily nested list, you will always need some kind of check to test if an element is itself an iterable or a leaf node. I wouldn't combine the flattening with computing the sum in one function, but rather define a generator function that only does the flattening:
def flatten(x):
try:
it = iter(x)
except TypeError:
yield x
else:
for i in it:
for j in flatten(i):
yield j
This way, you will have all the ugly bits contained in a single function. For a nested sequence x, you can now do
sum(flatten(x))
to get the recursive sum.
Things that are true of a list:
>>> import collections
>>> hasattr(element, '__getitem__')
True
>>> not hasattr(element, 'keys')
True
>>> isinstance(element, collections.Sequence)
True
>>> hasattr(element, '__iter__')
True
Things that are true of a string:
>>> string = '1234'
>>> hasattr(string, '__getitem__')
True
>>> not hasattr(string, 'keys')
True
>>> isinstance(string, collections.Sequence)
True
>>> hasattr(string, '__iter__')
False
What you see here isn't polymorphism in any language I know. += for lists means one thing, for numbers another thing. You'd like += for lists to mean something unusual (sum up all elements and return the sum) - but this is only meaningful for your specific example. For other (most, I'd say) uses of lists, the original meaning of += is much more convenient.
To make this behave truly polymorphically, you can derive from list and make += mean what you want - then you won't need these hacks.
BTW:
if type(element) == type([]):
Should be rewritten to:
if isinstance(element, list):
The purpose of this function is not to be universally applicable for adding nested structures, it was simply created to illustrate recursion.
Adding more complex sequence type checking, try and except, or the ability to add something other than numbers would make the function less useful as a learning tool for recursion.
That being said, isinstance(element, (list, tuple)) would probably be more appropriate here, and it wouldn't add any complexity.
You were checking if the element can be added to a int, which is not what you wanted.
The try is not bad though: Try to use it as a iterable - if it works then it is a iterable:
def recursive_sum(nested_sum_list):
sum = 0
# this raises TypeError if element is not a sequence
for element in nested_num_list:
try:
sum += recursive_sum(element)
except TypeError:
sum += element
return sum
There is also a typeclass for iterables:
import collections
print isinstance(element, collections.Iterable)
which basically just searches for a __iter__ method.