Python: Determine whether list of lists contains a defined sequence - python

I have a list of sublists, and I want to see if any of the integer values from the first sublist plus one are contained in the second sublist. For all such values, I want to see if that value plus one is contained in the third sublist, and so on, proceeding in this fashion across all sublists. If there is a way of proceeding in this fashion from the first sublist to the last sublist, I wish to return True; otherwise I wish to return False. For each value in the first sublist, I want to see if one can find that value plus n in each sublist reading left to right, where n is the index value of that sublist within the larger list.. (Sorry for the clumsy phrasing--I'm not sure how to clean up my language without using many more words.)
EDIT: (The previous articulation of desired output is ambiguous.) For each value in the first sublist, I want to see if one can find that value plus n in each sublist reading left to right, where n is the index value of that sublist within the larger list.
Here's what I wrote.
a = [ [1,3],[2,4],[3,5],[6],[7] ]
def find_list_traversing_walk(l):
for i in l[0]:
index_position = 0
first_pass = 1
walking_current_path = 1
while walking_current_path == 1:
if first_pass == 1:
first_pass = 0
walking_value = i
if walking_value+1 in l[index_position + 1]:
index_position += 1
walking_value += 1
if index_position+1 == len(l):
print "There is a walk across the sublists for initial value ", walking_value - index_position
return True
else:
walking_current_path = 0
return False
print find_list_traversing_walk(a)
My question is: Have I overlooked something simple here, or will this function return True for all true positives and False for all true negatives? Are there easier ways to accomplish the intended task? I would be grateful for any feedback others can offer!

You can view the desired operation as a reduction on a sequence of sets where the reducing operation is the following function:
In [25]: def func(x, y):
....: return set([t+1 for t in x]) & set(y)
....:
That is, func takes two collections, adds 1 to each element in the first collection, and returns the intersection of that and the second collection. Apply this function sequentially across a. If the final result is not an empty set, the answer is True:
In [29]: a = [ [1,3],[2,4],[3,5],[6],[7] ]
In [30]: bool(reduce(func, a))
Out[30]: True
In [31]: b = [ [1],[2,4],[5] ]
In [32]: bool(reduce(func, b))
Out[32]: False
(bool converts a non-empty set to True and an empty set to False.)
Note that if the input is typically a very long sequence, and you expect to know that the answer is False within a few steps, this is not a very efficient solution, since it always traverses the entire input.

Are there easier ways to accomplish the intended task?
Yes, it will be easier to define a recursive solution:
def find(lst, index):
if index == len(lst)-1:
return True
for i in lst[index]:
if i+1 in lst[index+1]:
return find(lst, index+1)
else:
return False
a = [ [1,3],[2,4],[3,5],[6],[7] ]
print find(a, 0) # prints "True"
a = [ [1,3],[2,4],[3,5],[6],[7], [9] ]
print find(a, 0) # prints "False"
UPDATE
What you describe in your comment is a bit more complex (the need for the next+1 to be one of the "guys" that were used before) - but not much:
def find(lst, part_res, index):
if index == len(lst)-1:
return True
found = False
new_part_res = []
for i in part_res:
if i+1 in lst[index+1]:
found = True
new_part_res.append(i+1)
if not found:
return False
else:
return find(lst, new_part_res, index+1)
a = [ [1,3],[2,4],[3,5],[6],[7] ]
print find(a, a[0], 0) # True
a = [ [1],[2,4],[5] ]
print find(a, a[0], 0) # False

I also thought it was expressed better recursively. If I understood your problem correctly, this is what I came up with.
a = [ [1,3],[2,4],[3,5],[6],[7] ]
b = [ [1],[2,4],[5] ]
c = [ [1,3],[2,4],[3,5],[6, 4],[5, 7] ]
def foo(data):
if len(data) == 1:
return True
return all(item + 1 in data[1] for item in data[0]) and foo(data[1:])
print foo(a), foo(b), foo(c)
>>>
False False True
>>>

Related

Remove equal characters from two python strings

I am writing a Python code to remove equal same characters from two strings which lies on the same indices. For example remove_same('ABCDE', 'ACBDE') should make both arguments as BC and CB. I know that string is immutable here so I have converted them to list. I am getting an out of index error.
def remove_same(l_string, r_string):
l_list = list(l_string)
r_list = list(r_string)
i = 0
while i != len(l_list):
print(f'in {i} length is {len(l_list)}')
while l_list[i] == r_list[i]:
l_list.pop(i)
r_list.pop(i)
if i == len(l_list) - 1:
break
if i != len(l_list):
i += 1
return l_list[0] == r_list[0]
I would avoid using a while loop in that case, I think this is a better and more clear solution:
def remove_same(s1, s2):
l1 = list(s1)
l2 = list(s2)
out1 = []
out2 = []
for c1, c2 in zip(l1, l2):
if c1 != c2:
out1.append(c1)
out2.append(c2)
s1_out = "".join(out1)
s2_out = "".join(out2)
print(s1_out)
print(s2_out)
It could be shortened using some list comprehensions but I was trying to be as explicit as possible
I feel this could be a problem.
while l_list[i] == r_list[i]:
l_list.pop(i)
r_list.pop(i)
This could reduce size of list and it can go below i.
Do a dry run on this, if l_list = ["a"] and r_list = ["a"].
It is in general not a good idea to modify a list in a loop. Here is a cleaner, more Pythonic solution. The two strings are zipped and processed in parallel. Each pair of equal characters is discarded, and the remaining characters are arranged into new strings.
a = 'ABCDE'
b = 'ACFDE'
def remove_same(s1, s2):
return ["".join(s) for s
in zip(*[(x,y) for x,y in zip(s1,s2) if x!=y])]
remove_same(a, b)
#['BC', 'CF']
Here you go:
def remove_same(l_string, r_string):
# if either string is empty, return False
if not l_string or not r_string:
return False
l_list = list(l_string)
r_list = list(r_string)
limit = min(len(l_list), len(r_list))
i = 0
while i < limit:
if l_list[i] == r_list[i]:
l_list.pop(i)
r_list.pop(i)
limit -= 1
else:
i += 1
return l_list[0] == r_list[0]
print(remove_same('ABCDE', 'ACBDE'))
Output:
False

Is there a way to check if a list is a sublist of another list? [duplicate]

I want to write a function that determines if a sublist exists in a larger list.
list1 = [1,0,1,1,1,0,0]
list2 = [1,0,1,0,1,0,1]
#Should return true
sublistExists(list1, [1,1,1])
#Should return false
sublistExists(list2, [1,1,1])
Is there a Python function that can do this?
Let's get a bit functional, shall we? :)
def contains_sublist(lst, sublst):
n = len(sublst)
return any((sublst == lst[i:i+n]) for i in xrange(len(lst)-n+1))
Note that any() will stop on first match of sublst within lst - or fail if there is no match, after O(m*n) ops
If you are sure that your inputs will only contain the single digits 0 and 1 then you can convert to strings:
def sublistExists(list1, list2):
return ''.join(map(str, list2)) in ''.join(map(str, list1))
This creates two strings so it is not the most efficient solution but since it takes advantage of the optimized string searching algorithm in Python it's probably good enough for most purposes.
If efficiency is very important you can look at the Boyer-Moore string searching algorithm, adapted to work on lists.
A naive search has O(n*m) worst case but can be suitable if you cannot use the converting to string trick and you don't need to worry about performance.
No function that I know of
def sublistExists(list, sublist):
for i in range(len(list)-len(sublist)+1):
if sublist == list[i:i+len(sublist)]:
return True #return position (i) if you wish
return False #or -1
As Mark noted, this is not the most efficient search (it's O(n*m)). This problem can be approached in much the same way as string searching.
My favourite simple solution is following (however, its brutal-force, so i dont recommend it on huge data):
>>> l1 = ['z','a','b','c']
>>> l2 = ['a','b']
>>>any(l1[i:i+len(l2)] == l2 for i in range(len(l1)))
True
This code above actually creates all possible slices of l1 with length of l2, and sequentially compares them with l2.
Detailed explanation
Read this explanation only if you dont understand how it works (and you want to know it), otherwise there is no need to read it
Firstly, this is how you can iterate over indexes of l1 items:
>>> [i for i in range(len(l1))]
[0, 1, 2, 3]
So, because i is representing index of item in l1, you can use it to show that actuall item, instead of index number:
>>> [l1[i] for i in range(len(l1))]
['z', 'a', 'b', 'c']
Then create slices (something like subselection of items from list) from l1 with length of2:
>>> [l1[i:i+len(l2)] for i in range(len(l1))]
[['z', 'a'], ['a', 'b'], ['b', 'c'], ['c']] #last one is shorter, because there is no next item.
Now you can compare each slice with l2 and you see that second one matched:
>>> [l1[i:i+len(l2)] == l2 for i in range(len(l1))]
[False, True, False, False] #notice that the second one is that matching one
Finally, with function named any, you can check if at least one of booleans is True:
>>> any(l1[i:i+len(l2)] == l2 for i in range(len(l1)))
True
The efficient way to do this is to use the Boyer-Moore algorithm, as Mark Byers suggests. I have done it already here: Boyer-Moore search of a list for a sub-list in Python, but will paste the code here. It's based on the Wikipedia article.
The search() function returns the index of the sub-list being searched for, or -1 on failure.
def search(haystack, needle):
"""
Search list `haystack` for sublist `needle`.
"""
if len(needle) == 0:
return 0
char_table = make_char_table(needle)
offset_table = make_offset_table(needle)
i = len(needle) - 1
while i < len(haystack):
j = len(needle) - 1
while needle[j] == haystack[i]:
if j == 0:
return i
i -= 1
j -= 1
i += max(offset_table[len(needle) - 1 - j], char_table.get(haystack[i]));
return -1
def make_char_table(needle):
"""
Makes the jump table based on the mismatched character information.
"""
table = {}
for i in range(len(needle) - 1):
table[needle[i]] = len(needle) - 1 - i
return table
def make_offset_table(needle):
"""
Makes the jump table based on the scan offset in which mismatch occurs.
"""
table = []
last_prefix_position = len(needle)
for i in reversed(range(len(needle))):
if is_prefix(needle, i + 1):
last_prefix_position = i + 1
table.append(last_prefix_position - i + len(needle) - 1)
for i in range(len(needle) - 1):
slen = suffix_length(needle, i)
table[slen] = len(needle) - 1 - i + slen
return table
def is_prefix(needle, p):
"""
Is needle[p:end] a prefix of needle?
"""
j = 0
for i in range(p, len(needle)):
if needle[i] != needle[j]:
return 0
j += 1
return 1
def suffix_length(needle, p):
"""
Returns the maximum length of the substring ending at p that is a suffix.
"""
length = 0;
j = len(needle) - 1
for i in reversed(range(p + 1)):
if needle[i] == needle[j]:
length += 1
else:
break
j -= 1
return length
Here is the example from the question:
def main():
list1 = [1,0,1,1,1,0,0]
list2 = [1,0,1,0,1,0,1]
index = search(list1, [1, 1, 1])
print(index)
index = search(list2, [1, 1, 1])
print(index)
if __name__ == '__main__':
main()
Output:
2
-1
Here is a way that will work for simple lists that is slightly less fragile than Mark's
def sublistExists(haystack, needle):
def munge(s):
return ", "+format(str(s)[1:-1])+","
return munge(needle) in munge(haystack)
def sublistExists(x, y):
occ = [i for i, a in enumerate(x) if a == y[0]]
for b in occ:
if x[b:b+len(y)] == y:
print 'YES-- SUBLIST at : ', b
return True
if len(occ)-1 == occ.index(b):
print 'NO SUBLIST'
return False
list1 = [1,0,1,1,1,0,0]
list2 = [1,0,1,0,1,0,1]
#should return True
sublistExists(list1, [1,1,1])
#Should return False
sublistExists(list2, [1,1,1])
Might as well throw in a recursive version of #NasBanov's solution
def foo(sub, lst):
'''Checks if sub is in lst.
Expects both arguments to be lists
'''
if len(lst) < len(sub):
return False
return sub == lst[:len(sub)] or foo(sub, lst[1:])
def sublist(l1,l2):
if len(l1) < len(l2):
for i in range(0, len(l1)):
for j in range(0, len(l2)):
if l1[i]==l2[j] and j==i+1:
pass
return True
else:
return False
I know this might not be quite relevant to the original question but it might be very elegant 1 line solution to someone else if the sequence of items in both lists doesn't matter. The result below will show True if List1 elements are in List2 (regardless of order). If the order matters then don't use this solution.
List1 = [10, 20, 30]
List2 = [10, 20, 30, 40]
result = set(List1).intersection(set(List2)) == set(List1)
print(result)
Output
True
if iam understanding this correctly, you have a larger list, like :
list_A= ['john', 'jeff', 'dave', 'shane', 'tim']
then there are other lists
list_B= ['sean', 'bill', 'james']
list_C= ['cole', 'wayne', 'jake', 'moose']
and then i append the lists B and C to list A
list_A.append(list_B)
list_A.append(list_C)
so when i print list_A
print (list_A)
i get the following output
['john', 'jeff', 'dave', 'shane', 'tim', ['sean', 'bill', 'james'], ['cole', 'wayne', 'jake', 'moose']]
now that i want to check if the sublist exists:
for value in list_A:
value= type(value)
value= str(value).strip('<>').split()[1]
if (value == "'list'"):
print "True"
else:
print "False"
this will give you 'True' if you have any sublist inside the larger list.

looping through loops in python?

I'm trying to solve this problem on the easy section of coderbyte and the prompt is:
Have the function ArrayAdditionI(arr) take the array of numbers stored in arr and return the string true if any combination of numbers in the array can be added up to equal the largest number in the array, otherwise return the string false. For example: if arr contains [4, 6, 23, 10, 1, 3] the output should return true because 4 + 6 + 10 + 3 = 23. The array will not be empty, will not contain all the same elements, and may contain negative numbers.
Here's my solution.
def ArrayAddition(arr):
arr = sorted(arr, reverse=True)
large = arr.pop(0)
storage = 0
placeholder = 0
for r in range(len(arr)):
for n in arr:
if n + storage == large: return True
elif n + storage < large: storage += n
else: continue
storage = 0
if placeholder == 0: placeholder = arr.pop(0)
else: arr.append(placeholder); placeholder = arr.pop(0)
return False
print ArrayAddition([2,95,96,97,98,99,100])
I'm not even sure if this is correct, but it seems to cover all the numbers I plug in. I'm wondering if there is a better way to solve this through algorithm which I know nothing of. I'm thinking a for within a for within a for, etc loop would do the trick, but I don't know how to do that.
What I have in mind is accomplishing this with A+B, A+C, A+D ... A+B+C ... A+B+C+D+E
e.g)
for i in range(len(arr):
print "III: III{}III".format(i)
storage = []
for j in range(len(arr):
print "JJ: II({}),JJ({})".format(i,j)
for k in range(len(arr):
print "K: I{}, J{}, K{}".format(i,j,k)
I've searched all over and found the suggestion of itertool, but I'm wondering if there is a way to write this code up more raw.
Thanks.
A recursive solution:
def GetSum(n, arr):
if len(arr) == 0 and n != 0:
return False
return (n == 0 or
GetSum(n, arr[1:]) or
GetSum(n-arr[0], arr[1:]))
def ArrayAddition(arr):
arrs = sorted(arr)
return GetSum(arrs[-1], arrs[:-1])
print ArrayAddition([2,95,96,97,98,99,100])
The GetSum function returns False when the required sum is non-zero and there are no items in the array. Then it checks for 3 cases:
If the required sum, n, is zero then the goal is achieved.
If we can get the sum with the remaining items after the first item is removed, then the goal is achieved.
If we can get the required sum minus the first element of the list on the rest of the list the goal is achieved.
Your solution doesn't work.
>>> ArrayAddition([10, 11, 20, 21, 30, 31, 60])
False
The simple solution is to use itertools to iterate over all subsets of the input (that don't contain the largest number):
def subsetsum(l):
l = list(l)
target = max(l)
l.remove(l)
for subset_size in xrange(1+len(l)):
for subset in itertools.combinations(l, subset_size):
if sum(subset) == target:
return True
return False
If you want to avoid itertools, you'll need to generate subsets directly. That can be accomplished by counting in binary and using the set bits to determine which elements to pick:
def subsetsum(l):
l = list(l)
target = max(l)
l.remove(l)
for subset_index in xrange(2**len(l)):
subtotal = 0
for i, num in enumerate(l):
# If bit i is set in subset_index
if subset_index & (1 << i):
subtotal += num
if subtotal == target:
return True
return False
Update: I forgot that you want to check all possible combinations. Use this instead:
def ArrayAddition(l):
for length in range(2, len(l)):
for lst in itertools.combinations(l, length):
if sum(lst) in l:
print(lst, sum(lst))
return True
return False
One-liner solution:
>>> any(any(sum(lst) in l for lst in itertools.combinations(l, length)) for length in range(2, len(l)))
Hope this helps!
Generate all the sums of the powerset and test them against the max
def ArrayAddition(L):
return any(sum(k for j,k in enumerate(L) if 1<<j&i)==max(L) for i in range(1<<len(L)))
You could improve this by doing some preprocessing - find the max first and remove it from L
One more way to do it...
Code:
import itertools
def func(l):
m = max(l)
rem = [itertools.combinations([x for x in l if not x == m],i) for i in range(2,len(l)-1)]
print [item for i in rem for item in i if sum(item)==m ]
if __name__=='__main__':
func([1,2,3,4,5])
Output:
[(1, 4), (2, 3)]
Hope this helps.. :)
If I understood the question correctly, simply this should return what you want:
2*max(a)<=sum(a)

Python: Check the occurrences in a list against a value

lst = [1,2,3,4,1]
I want to know 1 occurs twice in this list, is there any efficient way to do?
lst.count(1) would return the number of times it occurs. If you're going to be counting items in a list, O(n) is what you're going to get.
The general function on the list is list.count(x), and will return the number of times x occurs in a list.
Are you asking whether every item in the list is unique?
len(set(lst)) == len(lst)
Whether 1 occurs more than once?
lst.count(1) > 1
Note that the above is not maximally efficient, because it won't short-circuit -- even if 1 occurs twice, it will still count the rest of the occurrences. If you want it to short-circuit you will have to write something a little more complicated.
Whether the first element occurs more than once?
lst[0] in lst[1:]
How often each element occurs?
import collections
collections.Counter(lst)
Something else?
For multiple occurrences, this give you the index of each occurence:
>>> lst=[1,2,3,4,5,1]
>>> tgt=1
>>> found=[]
>>> for index, suspect in enumerate(lst):
... if(tgt==suspect):
... found.append(index)
...
>>> print len(found), "found at index:",", ".join(map(str,found))
2 found at index: 0, 5
If you want the count of each item in the list:
>>> lst=[1,2,3,4,5,2,2,1,5,5,5,5,6]
>>> count={}
>>> for item in lst:
... count[item]=lst.count(item)
...
>>> count
{1: 2, 2: 3, 3: 1, 4: 1, 5: 5, 6: 1}
def valCount(lst):
res = {}
for v in lst:
try:
res[v] += 1
except KeyError:
res[v] = 1
return res
u = [ x for x,y in valCount(lst).iteritems() if y > 1 ]
u is now a list of all values which appear more than once.
Edit:
#katrielalex: thank you for pointing out collections.Counter, of which I was not previously aware. It can also be written more concisely using a collections.defaultdict, as demonstrated in the following tests. All three methods are roughly O(n) and reasonably close in run-time performance (using collections.defaultdict is in fact slightly faster than collections.Counter).
My intention was to give an easy-to-understand response to what seemed a relatively unsophisticated request. Given that, are there any other senses in which you consider it "bad code" or "done poorly"?
import collections
import random
import time
def test1(lst):
res = {}
for v in lst:
try:
res[v] += 1
except KeyError:
res[v] = 1
return res
def test2(lst):
res = collections.defaultdict(lambda: 0)
for v in lst:
res[v] += 1
return res
def test3(lst):
return collections.Counter(lst)
def rndLst(lstLen):
r = random.randint
return [r(0,lstLen) for i in xrange(lstLen)]
def timeFn(fn, *args):
st = time.clock()
res = fn(*args)
return time.clock() - st
def main():
reps = 5000
res = []
tests = [test1, test2, test3]
for t in xrange(reps):
lstLen = random.randint(10,50000)
lst = rndLst(lstLen)
res.append( [lstLen] + [timeFn(fn, lst) for fn in tests] )
res.sort()
return res
And the results, for random lists containing up to 50,000 items, are as follows:
(Vertical axis is time in seconds, horizontal axis is number of items in list)
Another way to get all items that occur more than once:
lst = [1,2,3,4,1]
d = {}
for x in lst:
d[x] = x in d
print d[1] # True
print d[2] # False
print [x for x in d if d[x]] # [1]
You could also sort the list which is O(n*log(n)), then check the adjacent elements for equality, which is O(n). The result is O(n*log(n)). This has the disadvantage of requiring the entire list be sorted before possibly bailing when a duplicate is found.
For a large list with a relatively rare duplicates, this could be the about the best you can do. The best way to approach this really does depend on the size of the data involved and its nature.

counting odd numbers in a list python

This is a part of my homework assignment and im close to the final answer but not quite yet. I need to write a function that counts odd numbers in a list.
Create a recursive function count_odd(l) which takes as its only argument a list of integers. The function will return a count of the number of list elements that are odd, i.e., not evenly divisible by 2.\
>>> print count_odd([])
0
>>> print count_odd([1, 3, 5])
3
>>> print count_odd([2, 4, 6])
0
>>> print count_odd([0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144])
8
Here is what i have so far:
#- recursive function count_odd -#
def count_odd(l):
"""returns a count of the odd integers in l.
PRE: l is a list of integers.
POST: l is unchanged."""
count_odd=0
while count_odd<len(l):
if l[count_odd]%2==0:
count_odd=count_odd
else:
l[count_odd]%2!=0
count_odd=count_odd+1
return count_odd
#- test harness
print count_odd([])
print count_odd([1, 3, 5])
print count_odd([2, 4, 6])
print count_odd([0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144])
Can u help explain what im missing. The first two test harness works fine but i cant get the final two. Thanks!
Since this is homework, consider this pseudo-code that just counts a list:
function count (LIST)
if LIST has more items
// recursive case.
// Add one for the current item we are counting,
// and call count() again to process the *remaining* items.
remaining = everything in LIST except the first item
return 1 + count(remaining)
else
// base case -- what "ends" the recursion
// If an item is removed each time, the list will eventually be empty.
return 0
This is very similar to what the homework is asking for, but it needs to be translate to Python and you must work out the correct recursive case logic.
Happy coding.
def count_odd(L):
return (L[0]%2) + count_odd(L[1:]) if L else 0
Are slices ok? Doesn't feel recursive to me, but I guess the whole thing is kind of against usual idioms (i.e. - recursion of this sort in Python):
def countOdd(l):
if l == list(): return 0 # base case, empty list means we're done
return l[0] % 2 + countOdd(l[1:]) # add 1 (or don't) depending on odd/even of element 0. recurse on the rest
x%2 is 1 for odds, 0 for evens. If you are uncomfortable with it or just don't understand it, use the following in place of the last line above:
thisElement = l[0]
restOfList = l[1:]
if thisElement % 2 == 0: currentElementOdd = 0
else: currentElementOdd = 1
return currentElementOdd + countOdd(restOfList)
PS - this is pretty recursive, see what your teacher says if you turn this in =P
>>> def countOdd(l):
... return fold(lambda x,y: x+(y&1),l,0)
...
>>> def fold(f,l,a):
... if l == list(): return a
... return fold(f,l[1:],f(a,l[0]))
All of the prior answers are subdividing the problem into subproblems of size 1 and size n-1. Several people noted that the recursive stack might easily blow out. This solution should keep the recursive stack size at O(log n):
def count_odd(series):
l = len(series) >> 1
if l < 1:
return series[0] & 1 if series else 0
else:
return count_odd(series[:l]) + count_odd(series[l:])
The goal of recursion is to divide the problem into smaller pieces, and apply the solution to the smaller pieces. In this case, we can check if the first number of the list (l[0]) is odd, then call the function again (this is the "recursion") with the rest of the list (l[1:]), adding our current result to the result of the recursion.
def count_odd(series):
if not series:
return 0
else:
left, right = series[0], series[1:]
return count_odd(right) + (1 if (left & 1) else 0)
Tail recursion
def count_odd(integers):
def iter_(lst, count):
return iter_(rest(lst), count + is_odd(first(lst))) if lst else count
return iter_(integers, 0)
def is_odd(integer):
"""Whether the `integer` is odd."""
return integer % 2 != 0 # or `return integer & 1`
def first(lst):
"""Get the first element from the `lst` list.
Return `None` if there are no elements.
"""
return lst[0] if lst else None
def rest(lst):
"""Return `lst` list without the first element."""
return lst[1:]
There is no tail-call optimization in Python, so the above version is purely educational.
The call could be visualize as:
count_odd([1,2,3]) # returns
iter_([1,2,3], 0) # could be replaced by; depth=1
iter_([2,3], 0 + is_odd(1)) if [1,2,3] else 0 # `bool([1,2,3])` is True in Python
iter_([2,3], 0 + True) # `True == 1` in Python
iter_([2,3], 1) # depth=2
iter_([3], 1 + is_odd(2)) if [2,3] else 1
iter_([3], 1 + False) # `False == 0` in Python
iter_([3], 1) # depth=3
iter_([], 1 + is_odd(3)) if [3] else 1
iter_([], 2) # depth=4
iter_(rest([]), 2 + is_odd(first([])) if [] else 2 # bool([]) is False in Python
2 # the answer
Simple trampolining
To avoid 'max recursion depth exceeded' errors for large arrays all tail calls in recursive functions can be wrapped in lambda: expressions; and special trampoline() function can be used to unwrap such expressions. It effectively converts recursion into iterating over a simple loop:
import functools
def trampoline(function):
"""Resolve delayed calls."""
#functools.wraps(function)
def wrapper(*args):
f = function(*args)
while callable(f):
f = f()
return f
return wrapper
def iter_(lst, count):
#NOTE: added `lambda:` before the tail call
return (lambda:iter_(rest(lst), count+is_odd(first(lst)))) if lst else count
#trampoline
def count_odd(integers):
return iter_(integers, 0)
Example:
count_odd([1,2,3])
iter_([1,2,3], 0) # returns callable
lambda:iter_(rest(lst), count+is_odd(first(lst))) # f = f()
iter_([2,3], 0+is_odd(1)) # returns callable
lambda:iter_(rest(lst), count+is_odd(first(lst))) # f = f()
iter_([3], 1+is_odd(2)) # returns callable
lambda:iter_(rest(lst), count+is_odd(first(lst))) # f = f()
iter_([], 1+is_odd(3))
2 # callable(2) is False
I would write it like this:
def countOddNumbers(numbers):
sum = 0
for num in numbers:
if num%2!=0:
sum += numbers.count(num)
return sum
not sure if i got your question , but as above something similar:
def countOddNumbers(numbers):
count=0
for i in numbers:
if i%2!=0:
count+=1
return count
Generator can give quick result in one line code:
sum((x%2 for x in nums))

Categories

Resources