Lists in Python (how to add the elements) - python

I just started with Python. Had Haskell before. In Haskell, I worked most of the time with/on lists. In Python I want do so.
I have a list of:
l = [1,2,3,4]
How can I add the 4 elements in the list, so that I get 10 as result (1+2+3+4)
I need a recursive function and an iterative (not clean and stable as iterative , but nevertheless).
In Haskell I did this:
sum [] = 0
sumlist(x:xs) = x + sumlist xs
In Python I tried this:
def sumlist(l)
if l == 0: #or Nil, i do not know
result 0
else:
l [0] + sumlist(l)
but that does not work, maybe I am still to focused on Haskell's implementation style.
Would be great if I get some help.

Edit:
If you do not wish to use sum, you can make your own function1:
>>> def sumlist(seq, start=0):
... return sumlist(seq[1:], start + seq[0]) if seq else start
...
>>> lst = [1, 2, 3, 4]
>>> sumlist(lst)
10
>>> lst = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> sumlist(lst)
45
>>> sumlist(lst, 10) # You can specify a number to start at too
55
>>>
1Note: It is assumed that you will pass in a sequence of numbers.
Just use the sum built-in:
>>> l = [1, 2, 3, 4]
>>> sum(l)
10
>>>
From the docs:
sum(iterable[, start])
Sums start and the items of an iterable from left to right and returns the total. start defaults to 0. The iterable's items are
normally numbers, and the start value is not allowed to be a string.

I haven't used Haskell, but in Python objects are rich in that they know how to work with other objects. You can think of it like the "interface between objects" is well understood, and everyone is expected to behave nicely:
EAFP
Easier to ask for forgiveness than permission. This common Python
coding style assumes the existence of valid keys or attributes and
catches exceptions if the assumption proves false. This clean and fast
style is characterized by the presence of many try and except
statements. The technique contrasts with the LBYL style common to many
other languages such as C.
So the idea is if there a function in the standard library called sum, you can be sure it does at least two things:
Knows how to sum up things it can sum up.
If it doesn't know how to sum it up, it will raise an Exception.
Once you understand how this works, you can start passing anything to sum to see what it does:
>>> sum((1,2,3)) # a list
6
>>> sum([1,2,3]) # a tuple
6
>>> sum((1,)) # a tuple with one object
1
>>> sum([2]) # a list with one object
2
So as long as the item is iterable, sum can do its thing. Of course, when you pass it something it cannot work with, you get the appropriate exception:
>>> sum(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> sum('1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Note that a string is iterable in Python, which is why you get a different exception.
After you comment here:
Is there another way summing them up , or is it just this one ?
Its worth pointing out the zen of python which is kind of a guide on what makes code "pythonic", which states:
There should be one-- and preferably only one --obvious way to do it.
So the obvious way to sum things up would be with a function named sum.

Your code isn't actually too far off. empty lists are "falsy", so you can do something like:
def sumlist(lst):
if not lst:
return 0
else:
# Add the first element to the sum of the remaining elements.
return lst[0] + sumlist(lst[1:])
Of course, I'm assuming that you're doing this as part of a homework assignment where you're learning about recursion -- This would be horrible in any sort of production code where you should really just use sum (the builtin function).

If you are looking for a method other than the obvious sum you can use reduce:
>>> l = [1,2,3,4]
>>> import operator
>>> reduce(operator.add, l)
10
If you want another function, you could do:
def sum_l(l):
rtr=0
while l:
rtr+=l.pop()
return rtr
That function is destructive to the list. If you want to keep the list, call it with a copy of the list:
n=sum_l(l[:])
Yet another (that does not destroy the list) is to use a for loop on the iterable:
def sum_l(l):
rtr=0
for e in l:
rtr+=e
return rtr

Here are two ways you can compute the sum:
Just use the in-built sum function:
sum(l)
Using a for loop:
sum_val = 0
for i in range(0, len(l)):
sum_val += l[i]
Using recursion to compute a sum is just waste of computing resources. Do not use recursion until its absolutely necessary.

Related

Why does Python allow out-of-range slice indexes for sequences?

So I just came across what seems to me like a strange Python feature and wanted some clarification about it.
The following array manipulation somewhat makes sense:
p = [1,2,3]
p[3:] = [4]
p = [1,2,3,4]
I imagine it is actually just appending this value to the end, correct?
Why can I do this, however?
p[20:22] = [5,6]
p = [1,2,3,4,5,6]
And even more so this:
p[20:100] = [7,8]
p = [1,2,3,4,5,6,7,8]
This just seems like wrong logic. It seems like this should throw an error!
Any explanation?
-Is it just a weird thing Python does?
-Is there a purpose to it?
-Or am I thinking about this the wrong way?
Part of question regarding out-of-range indices
Slice logic automatically clips the indices to the length of the sequence.
Allowing slice indices to extend past end points was done for convenience. It would be a pain to have to range check every expression and then adjust the limits manually, so Python does it for you.
Consider the use case of wanting to display no more than the first 50 characters of a text message.
The easy way (what Python does now):
preview = msg[:50]
Or the hard way (do the limit checks yourself):
n = len(msg)
preview = msg[:50] if n > 50 else msg
Manually implementing that logic for adjustment of end points would be easy to forget, would be easy to get wrong (updating the 50 in two places), would be wordy, and would be slow. Python moves that logic to its internals where it is succint, automatic, fast, and correct. This is one of the reasons I love Python :-)
Part of question regarding assignments length mismatch from input length
The OP also wanted to know the rationale for allowing assignments such as p[20:100] = [7,8] where the assignment target has a different length (80) than the replacement data length (2).
It's easiest to see the motivation by an analogy with strings. Consider, "five little monkeys".replace("little", "humongous"). Note that the target "little" has only six letters and "humongous" has nine. We can do the same with lists:
>>> s = list("five little monkeys")
>>> i = s.index('l')
>>> n = len('little')
>>> s[i : i+n ] = list("humongous")
>>> ''.join(s)
'five humongous monkeys'
This all comes down to convenience.
Prior to the introduction of the copy() and clear() methods, these used to be popular idioms:
s[:] = [] # clear a list
t = u[:] # copy a list
Even now, we use this to update lists when filtering:
s[:] = [x for x in s if not math.isnan(x)] # filter-out NaN values
Hope these practical examples give a good perspective on why slicing works as it does.
The documentation has your answer:
s[i:j]: slice of s from i to j (note (4))
(4) The slice of s from i to j is defined as the sequence of items
with index k such that i <= k < j. If i or j is greater than
len(s), use len(s). If i is omitted or None, use 0. If j
is omitted or None, use len(s). If i is greater than or equal to
j, the slice is empty.
The documentation of IndexError confirms this behavior:
exception IndexError
Raised when a sequence subscript is out of range. (Slice indices are silently truncated to fall in the allowed range; if an index is
not an integer, TypeError is raised.)
Essentially, stuff like p[20:100] is being reduced to p[len(p):len(p]. p[len(p):len(p] is an empty slice at the end of the list, and assigning a list to it will modify the end of the list to contain said list. Thus, it works like appending/extending the original list.
This behavior is the same as what happens when you assign a list to an empty slice anywhere in the original list. For example:
In [1]: p = [1, 2, 3, 4]
In [2]: p[2:2] = [42, 42, 42]
In [3]: p
Out[3]: [1, 2, 42, 42, 42, 3, 4]

Generator error in python

I'm still new to generators in python. I was trying out one on my own and tried to something really simple:
def fib(a):
... if a==0 or a==1:return 1
... yield fib(a-1)+fib(a-2)
print(list(fib(5))
This code gave me this error:
TypeError: unsupported operand type(s) for +: 'generator' and 'generator'
Can't generators be used in this manner?
Calling a generator function doesn't produce the next value. It produces a generator object, a specialist version of an iterator object. You have, in effect, something that wraps a paused function.
To get another value from that function, you'd have to call next(iterator) on the object or use something like list(iteratort) or for ... in iterator to loop over the object and get the values out. See What does the "yield" keyword do? for more details.
So here, the you'd have to use next(fib(a-1)) + next(fib(a-2)) to get the two recursive values out. That'll also fail, because your termination case (a == 0 or a == 1) uses return (translated into the value of a StopIteration exception) and not yield; you'd have to fix that too.
And this highlights why your recursive function should not be a generator function. Your function doesn't produce a series of values to iterate over. There's just one result mfor a given argument value. You'd be far better off to just use return and not yield.
If you wanted to generate a sequence of fibonacci numbers, the function argument would need to be seen as a limit; "give me the first n fibonacci numbers". The following iterative function does that:
def first_n_fibonacci(n):
a, b = 0, 1
for i in range(0, n):
a, b = b, a + b
yield a
This would give you a list of the first n fibonacci numbers, as a generator:
>>> f = first_n_fibonacci(5)
>>> f
<generator object first_n_fibonacci at 0x10b2c8678>
>>> next(f)
1
>>> next(f)
1
>>> list(f)
[2, 3, 5]
or you could use the argument to produce all fibonacci values up to a limit, or to produce an endless generator of fibonacci numbers. None of those would require recursion, a loop like the above suffices.
Generators are meant to be used for iterations, and yet by adding two of the returning values from fib you are trying to use it as a scalar value, which is confirmed by your terminal condition (where a equals to 0 or 1) also returning a scalar value.
You should simply use return in this case.
def fib(a):
if a==0 or a==1:return 1
return fib(a-1)+fib(a-2)
print(fib(5))
If you do want to use a generator, you need to think about outputting a sequence, and not a single value. Do you want your fib(a) to output the ath fib number or the the 1st, 2nd, 3rd, 4th, 5th ... ath fib number? If the latter, then generators are good for this.
Here is an example generator for the Fibonacci numbers from the 1st to the nth number.
def fib(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b

function to add elements in list

I'm creating a program that adds the elements of the same cardinality in two separate lists:
list1 = [1,2,3]
list2 = [2,3,4]
Thus list3 would be [3,5,7]
This is the code I have:
def add_list(lst1,lst2):
sum_lst = []
index = 0
while (len(lst1)-1) >= index:
sum_lst[index] == lst1[index] + lst2[index]
index += 1
return sum_lst
I get this error "index out of range" when I run it:
sum_lst[index] == lst1[index] + lst2[index]
How is it out of range considering that I stop the index before it reaches past the length of the list?
sum_lst[index] == lst1[index] + lst2[index]
^1 ^2
2 main issues:
you initialize sum_lst as '[]` which means, YES, 0 IS out of index.
== is not the same as =. == is a boolean expression operation that asses equality whereas = is the assignment operator.
two fixes:
#replaces that one line
sum_lst.append(lst1[index]+lst2[index])
OR
#replaces the whole function
sum_lst = [x+y for x,y in zip(lst1,lst2)]
The reason you're running into your error message is because sum_lst doesn't yet have the indices you're looking for.
E.g. if you try
>>> some_list = []
>>> some_list[0] = 1 # you'll get
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
Instead, try something like:
sum_lst.append(lst1[index] + lst2[index])
This'll add a new element to the end of the array
The problem with this line was already pointed out:
sum_lst[index] == lst1[index] + lst2[index]
In addition to this fix, I'm going to explain a few steps you could take to improve this code and similar code.
Instead of incrementing an index and using a while loop, it's common practice to use a for loop in Python. We can use the range function to loop over all indexes.
Here's an improvement (note we fixed append and the == issue here):
def add_list(lst1, lst2):
sum_lst = []
for index in range(len(lst1)):
sum_lst.append(lst1[index] + lst2[index])
return sum_lst
But that still isn't idiomatic.
It would be even better to use the enumerate function to loop over items while retrieving their indices. Whenever you see something like for i in range(len(lst1)) think enumerate.
def add_list(lst1, lst2):
sum_lst = []
for index, item1 in enumerate(lst1):
sum_lst.append(item1 + lst2[index])
return sum_lst
The only reason we need the index is because we're trying to loop over two lists at once. Whenever you need to loop over multiple lists at the same time, you can use the zip function to zip iterables/lists together:
def add_list(lst1, lst2):
sum_lst = []
for item1, item2 in zip(lst1, lst2):
sum_lst.append(item1 + item2)
return sum_lst
This is nearly the best we could do. There's just one more improvement we could make: use a list comprehension instead of a for loop:
def add_list(lst1, lst2):
return [item1 + item2 for item1, item2 in zip(lst1, lst2)]
>>> [a+b for a,b in zip(list1,list2)]
[3, 5, 7]
I'd say the main problem with your code isn't the bug itself, it's that you're writing in a traditionally procedural pattern. Perhaps you used to do Java or C? Python is a multi-paradigm language, and supports functional programming. The main takeaway is that you should try to keep things immutable, so instead of creating empty lists and gradually populating them with content, try to create the resulting list on the fly. As in the example above. That way you won't be able produce indexing errors on either side of the assignment operator.
But, if you really want to know, your bug is two-fold:
>>> a = []
>>> a[1] = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
I.e. you can't append things to a list by assigning to a new element. You could do it this way instead:
a += [3]
Secondly you've used a comparison operator (==) instead of the assignment operator (=).

Check if iterator is sorted

I have an iterator it that I'm assuming already sorted but I would like to raise an exception if it isn't.
Data from iterator is not in memory so I do not want to use sorted() builtin because AFAIK it puts the whole iterator in a list.
The solution I'm using now is to wrap the iterator in a generator function like this:
def checkSorted(it):
prev_v = it.next()
yield prev_v
for v in it:
if v >= prev_v:
yield v
prev_v = v
else:
raise ValueError("Iterator is not sorted")
So that I can use it like this:
myconsumer(checkSorted(it))
Does someone know if there are better solutions?
I know that my solution works but it seems quite strange (at least to me) writing a module on my own to accomplish such a trivial task. I'm looking for a simple one liner or builtin solution (If it exists)
Basically your solution is almost as elegant as it gets (you could of course put it in an utility module if you find it generally useful). You could if you wanted it use an infinity object to cut the code down a bit, but then you have to include a class definition as well which grows the code again (unless you inline the class definition):
def checkSorted(it):
prev = type("", (), {"__lt__": lambda a, b: False})()
for x in it:
if prev < x:
raise ValueError("Not sorted")
prev = x
yield x
The first line is using the type to first create a class and then instantiate it. Objects of this class compares less than to anything (infinity object).
The problem with doing a one-liner is that you have to deal with three constructs: you have to update state (assignment), throw an exception and doing a loop. You could easily perform these by using statements, but making them into a oneliner will mean that you will have to try to put the statements on the same line - which in turn will result in problem with the loop and if-constructs.
If you want to put the whole thing into an expression you will have to use dirty tricks to do these, the assignment and looping the iterutils can provide and the throwing can be done by using the throw method in a generator (which can be provided in an expression too):
imap( lambda i, j: (i >= j and j or (_ for _ in ()).throw(ValueError("Not sorted"))), *(lambda pre, it: (chain([type("", (), {"__ge__": lambda a, b: True})()], pre), it))(*tee(it)))
The last it is the iterator you want to check and the expression evaluates to a checked iterator. I agree it's not good looking and not obvious what it does, but you asked for it (and I don't think you wanted it).
As an alternative i suggest to use itertools.izip_longest (and zip_longest in python 3 )to create a generator contains consecutive pairs :
You can use tee to create 2 independent iterators from a first iterable.
from itertools import izip_longest,tee
def checkSorted(it):
pre,it=tee(it)
next(it)
for i,j in izip_longest(pre,it):
if j:
if i >= j:
yield i
else:
raise ValueError("Iterator is not sorted")
else :
yield i
Demo :
it=iter([5,4,3,2,1])
print list(checkSorted(it))
[5, 4, 3, 2, 1]
it=iter([5,4,3,2,3])
print list(checkSorted(it))
Traceback (most recent call last):
File "/home/bluebird/Desktop/ex2.py", line 19, in <module>
print list(checkSorted(it))
File "/home/bluebird/Desktop/ex2.py", line 10, in checkSorted
raise ValueError("Iterator is not sorted")
ValueError: Iterator is not sorted
Note : Actually I think there is no need to yield the values of your iterable wen you have them already.So as a more elegant way I suggest to use a generator expression within all function and return a bool value :
from itertools import izip,tee
def checkSorted(it):
pre,it=tee(it)
next(it)
return all(i>=j for i,j in izip(pre,it))

Invalid syntax when creating lists

So i am attempting to create 3 different lists one of 1k, one of 10k, and one of 100k items long populated with random numbers from 1 to 10million but i cant seem to figure out why it keeps saying its invalid syntax. So far i have:
edit: okay it seems to have fixed the invalid syntax problem with some tweaking but still gives me:
Traceback (most recent call last):
File "C:/Python32/funtime.py", line 16, in <module>
print (list[1])
TypeError: 'type' object is not subscriptable
this is exactly what i have typed in:
import random
def createlist(a):
blist =[]
count=0
while count <= a:
blist.append(random.randint(1,10000000))
count= count+1
return blist;
list1= createlist(10000);
print (list[1])
Traceback (most recent call last):
File "C:/Python32/funtime.py", line 16, in <module>
print (list[1])
TypeError: 'type' object is not subscriptable
Objects of type type are indeed not subscriptable. list is the name of the builtin list class, which is an instance of type. It doesn't make any sense to subscript it, so that's clearly not what you intended to do.
You meant print (list1[1]). list1 is the name you bound to your list object created by createlist(10000).
The trick to finding these sort of bugs is to look at the error message Python gave you. It's telling you exactly the problem line, and exactly why it's a problem. All that was missing was the realisation that list is not the name of the object you wanted to subscript.
A much shorter version would be:
>>> import random
>>> def create_list(length):
... return [random.randint(1,1000000) for _ in range(length)]
This demonstrates a couple of things:
A much more compact way to loop x times (the for _ in range(length) idiom)
List comprehensions to create lists, instead of repeated calls to append.
The use of _ as a variable name when you need the variable, but not the actual data in it. This is a somewhat common convention that crops up most often in list comprehensions. It isn't a problem not to use it, but it crops up often enough that it pays to be aware of it.
Hat-tip to #mgilson for his comment on another answer that reminded me the _ convention.
The following works fine on my system.
import random
def createlist(a):
blist =[]
count= 0
while count <= a:
blist.append(random.randint(1,1000000))
count=count+1
return blist
list1= createlist(10000)
print list1[1]
http://ideone.com/SL2bL <-- try it here.
I'd probably do it like this
import random
def createlist(a):
return [random.randint(1,1000000) for i in xrange(a)]
list1= createlist(10000)
print list1[1]
Or probably just skip the function..
list1 = [random.randint(1,1000000) for i in xrange(a)]
print list1[1]
As some other answers have stated, you could easily use list comprehension for something like this (the so-called Pythonic way):
somelist = [random.randint(1, 1000000) for i in xrange(10000)]
List comprehension is fast in Python because it is executed by underlying C code. Moreover, using xrange is more memory-efficient.
Note: By convention, when the loop-control variable is not used in Python, it is named _:
somelist = [random.randint(1, 1000000) for _ in xrange(10000)]
use a list comprehension (its way betta and makes you look pro, haha)
[random.randint(1, 1000000) for i in range(10000)]
First, you don't put a paren by while unless you're evaluating complex expressions.
Next, you used a semicolon to return your blist. Not necessary.
Finally...why not use a for loop?
import random
def createlist(a):
blist =[]
for x in xrange(a):
blist.append(random.randint(1,1000000))
return blist
list1= createlist(10000)
print list1[0]

Categories

Resources