Replacing a function with something iterable - python

In short. How do I write something else than this: for another in combinationOfK(K-1, L[i+1:]): My function combinationOfK(...) is not iterable.
I am trying to understand the code from here, solution to. Problem 26: Generate the combinations of K distinct objects chosen from the N elements of a listI know what yield does. But I am trying to write the code without a yield statement. Code with yield statement is this.
def combination(K, L):
if K<=0:
yield []
return
for i in range(len(L)):
thisone = L[i:i+1]
for another in combination(K-1, L[i+1:]):
yield thisone + another
The question, yield-keyword-explained gave me the idea that I could replace yield. The recepie they give, which is not working for me, is:
When you see a function with yield statements, apply this easy
trick to understand what will happen:
Insert a line result = [] at the start of the function.
Replace each yield expr with result.append(expr).
Insert a line return result at the bottom of the function.
Yay - no more yield statements! Read and figure out code.
Revert function to original definition.
Using this to get code without yield give me this. The code is not working (the function is not iterable). What do I have to write to get this code working without yield?
def combinationOfK(K,L):
result = []
if K <= 0:
result.append([])
return
for i in range(len(L)):
thisone = L[i:i+1]
for another in combinationOfK(K-1, L[i+1:]): # the error
result.append(thisone + another)
return result
I am using this code to test the function,
the_list = ['a','b','c','d','e']
print list(combinationOfK(2, the_list))
raising error TypeError: 'NoneType' object is not iterable.

The problem is that your original code uses return in an unusual way.
def combination(K, L):
if K<=0:
yield []
return # <--- hmmm
Most of the time you won't see return in a generator, because you don't often need it. Usually, generators simply "fall off" at the end; the interpreter reaches the end of the generator without encountering a return statement, and then it knows to throw StopIteration.
In this case, the writer of the code has inserted a return statement to "hurry up" the process. When K <= 0, there's no more work to be done, so the generator can throw StopIteration -- but without the return statement, it would go into the for loop, producing incorrect results. In my opinion, a clearer way to do this would have been like so:
def combination(K, L):
if K<=0:
yield []
else:
for i in range(len(L)):
thisone = L[i:i+1]
for another in combination(K-1, L[i+1:]):
yield thisone + another
Now the conversion works as expected:
def combination2(K, L):
result = []
if K <= 0:
result.append([])
else:
for i in range(len(L)):
thisone = L[i:i + 1]
for another in combination2(K - 1, L[i + 1:]):
result.append(thisone + another)
return result

As Vincent mentioned, your function is returning None because of the 5th line. Change it to this:
def combinationOfK(K,L):
result = []
if K <= 0:
result.append([])
return result
for i in range(len(L)):
thisone = L[i:i+1]
for another in combinationOfK(K-1, L[i+1:]): # the error
result.append(thisone + another)
return result
However, why are you against yield? Generators make for readable, efficient code. The point of the Yield Keyword Explained article was not to dispense with it, but rather, to explain it.
In the generator code you posted:
def combination(K, L):
if K<=0:
yield []
return
for i in range(len(L)):
thisone = L[i:i+1]
for another in combination(K-1, L[i+1:]):
yield thisone + another
The return statement does NOT mean the same thing as return does in a normal function. In a generator, return immediately raises StopIteration, which causes the caller to stop iterating over the generator object.

Related

How to check elements created by generator when debugging a python project?

I use PyCharm to realize a program which is aimed to generate primes. Code like this:
def _odd_iter():
n = 1
while True:
yield n
n = n + 2
def _not_divisible(n):
return lambda x: x % n > 0
def primes():
it = _odd_iter()
yield 2
while True:
i = next(it)
yield i
it = filter(_not_divisible, it) # !!!!!!!!!!don't know how it works!!!!!!!!!!
for n in primes():
if n < 1000:
print(n)
else:
break
For me the annotated code is obscure, I dont know how it works and whether it is right, so I add a breakpoint on it and determine to debug. But it is a generator, I cannot see the detail numbers. What can I do?
filter(function or None, iterable) --> filter object
Return an iterator yielding those items of iterable for which function(item) is true. If function is None, return the items that are true.
You can't see the details because it returns an iterable object, do this to see what it returns:
it = list(filter(_not_divisible, it)) # or next(filter(...))

Recursively Generating a List of n choose k combinations in Python - BUT return a list

I'm attempting to generate all n choose k combinations of a list (not checking for uniqueness) recursively by following the strategy of either include or not include an element for each recursive call. I can definitely print out the combinations but I for the life of me cannot figure out how to return the correct list in Python. Here are some attempts below:
class getCombinationsClass:
def __init__(self,array,k):
#initialize empty array
self.new_array = []
for i in xrange(k):
self.new_array.append(0)
self.final = []
self.combinationUtil(array,0,self.new_array,0,k)
def combinationUtil(self,array,array_index,current_combo, current_combo_index,k):
if current_combo_index == k:
self.final.append(current_combo)
return
if array_index >= len(array):
return
current_combo[current_combo_index] = array[array_index]
#if current item included
self.combinationUtil(array,array_index+1,current_combo,current_combo_index+1,k)
#if current item not included
self.combinationUtil(array,array_index+1,current_combo,current_combo_index,k)
In the above example I tried to append the result to an external list which didn't seem to work. I also tried implementing this by recursively constructing a list which is finally returned:
def getCombinations(array,k):
#initialize empty array
new_array = []
for i in xrange(k):
new_array.append(0)
return getCombinationsUtil(array,0,new_array,0,k)
def getCombinationsUtil(array,array_index,current_combo, current_combo_index,k):
if current_combo_index == k:
return [current_combo]
if array_index >= len(array):
return []
current_combo[current_combo_index] = array[array_index]
#if current item included & not included
return getCombinationsUtil(array,array_index+1,current_combo,current_combo_index+1,k) + getCombinationsUtil(array,array_index+1,current_combo,current_combo_index,k)
When I tested this out for the list [1,2,3] and k = 2, for both implementations, I kept getting back the result [[3,3],[3,3],[3,3]]. However, if I actually print out the 'current_combo' variable within the inner (current_combo_index == k) if statement, the correct combinations print out. What gives? I am misunderstanding something to do with variable scope or Python lists?
The second method goes wrong because the line
return [current_combo]
returns a reference to current_combo. At the end of the program, all the combinations returned are references to the same current_combo.
You can fix this by making a copy of the current_combo by changing the line to:
return [current_combo[:]]
The first method fails for the same reason, you need to change:
self.final.append(current_combo)
to
self.final.append(current_combo[:])
Check this out: itertools.combinations. You can take a look at the implementation as well.

Writing filter() function but getting typeError

my code consists of me recreating the function 'filter()' and using it with a function to filter words longer than 5 characters. It worked with the actual function filter when I tried it btw...I'm using python 3+
def filter1(fn, a):
i = 0
while i != len(a):
u = i - 1
a[i] = fn(a[i], a[u])
i += 1
return a
def filter_long_words(l):
if len[l] > 5:
return [l]
listered = ['blue', 'hdfdhsf', 'dsfjbdsf', 'jole']
print(list(filter1(filter_long_words, listered)))
getting error
TypeError: filter_long_words() takes 1 positional argument but 2 were given
You are passing two parameters to fn (which refers to filter_long_words) here:
a[i] = fn(a[i], a[u])
But filter_long_words only accepts one parameter.
Notes:
You can loop through lists using for item in my_list, or if you want index as well for index, item in enumerate(my_list).
I think you might get an IndexError since u will be -1 in the first round of your loop.
The filter function can also be expressed as a list comprehension: (item for item in listered if filter_long_words(item))
My version of filter would look like this, if I have to use a for loop:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
for item in sequence:
if fn(item):
yield item
Since you have stated that you are using Python 3, this returns a generator instead of a list. If you want it to return a list:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
acc = []
for item in sequence:
if fn(item):
acc.append(item)
return acc
If you don't need to use a for loop:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
return (item for item in sequence if fn(item))
Your're calling fn with 2 parameters in filter1(fn, a), and since you've passed filter_long_words() to filter1 as fn, that triggers the error.
But there's more weird stuff:
I don't understand the magick of filter1 or what you were trying to
accomplish, but it seems to me that you don't have a clear idea what to do.
But if you want to mimic (somehow) how filter works, you have to return a
list which contains only items for which the fn function returns true. When
you know this, you can rewrite it - here are a few suggestions for rewrite
# explicit, inefficient and long, but straightforward version:
def filter1(fn, a):
new_list = []
for item in a:
if fn(item):
new_list.append(item):
return new_list
# shorter version using list comprehensions:
def filter1(fn, a):
return [item for item in a if fn(item)]
The filter_long_words function is wrong too - it should return True or
False. The only reason why it could work is because any non-empty list is
treated as True by python and default return value of a function is None,
which translates to False. But it's confusing and syntactically wrong to use
len[l] - the proper usage is len(l).
There are a few suggestions for rewrite, which all returns explicit boolean
values:
# unnecessary long, but self-explanatory:
def filter_long_words(l):
if len(l) > 5:
return True
else
return False
# short variant
def filter_long_words(l):
return len(l) > 5
You are calling "filter_long_words" with 2 parameter => fn(a[i], a[u]) also there is an error
def filter_long_words(l):
if **len[l]** > 5:
return [l]
len is builtin method it should be len(l)

Equivalent of `return` for Python generators

Sometimes, when rewriting recursive functions as generators, I miss the brevity of return.
"""
Returns a list of all length n strings that can be made out of a's and/or b's.
"""
def ab_star(n):
if n == 0:
return [""]
results = []
for s in ab_star(n - 1):
results.append("a" + s)
results.append("b" + s)
return results
turns into
"""
Generator for all length n strings that can be made out of a's and/or b's.
"""
def ab_star(n):
if n == 0:
yield ""
else:
for s in ab_star(n - 1):
yield "a" + s
yield "b" + s
It's that else that bugs me. I wish there was a way to say "yield, and this is it, so exit the function". Is there a way?
Don't miss return, use it.
You can return right after you yield.
def ab_star(n):
if n == 0:
yield ""
return
for s in ab_star(n - 1):
yield "a" + s
yield "b" + s
An alternative is to use return in both cases, where the first case returns a sequence of length 1, and the second returns a generator-expression:
def ab_star(n):
if n == 0:
return ( "", )
return ( c+s for s in ab_star(n - 1) for c in 'ab' )
This avoidance of yield avoids the limitation that you cannot use both return <value> and yield in the same function.
(This works in your case because your function doesn't have to be a generator. Since you only iterate over the results, it can also return a tuple.)
There isn't. When I wrote the "Simple Generators PEP", I noted:
Q. Then why not allow an expression on "return" too?
A. Perhaps we will someday. In Icon, "return expr" means both "I'm
done", and "but I have one final useful value to return too, and
this is it". At the start, and in the absence of compelling uses
for "return expr", it's simply cleaner to use "yield" exclusively
for delivering values.
But that never gained traction. Until it does ;-), you can make your generator look more like your first function by writing the first part as:
if n == 0:
yield ""
return
Then you can drop the else: statement and dedent the rest.

Instead of continue, rerun function

I'm wondering how to do the following in Python.
If I have a function with a for loop, it is possible to with an if statement to skip certain numbers.
This is an implementation of fisher-yates d got from activestate.com.
import random
def shuffle(ary):
a=len(ary)
b=a-1
for d in range(b,0,-1):
e=random.randint(0,d)
if e == d:
continue
ary[d],ary[e]=ary[e],ary[d]
return ary
Now continue simply goes to the next value for d. How can I, instead of doing continue, rerun the function with the original parameter ary?
Note that the function is just some example code, I'm curious on how to do this in general.
Also, maintaining a copy of the array might not be possible if the list is big, so thats not really a solution imo.
This is a common recursive pattern. However, your case is a little different than usual because here you need to make a copy of your input list to use when you recurse if the shuffling fails.:
import random
def shuffle(ary):
initial = ary[:]
a=len(ary)
b=a-1
for d in range(b,0,-1):
e=random.randint(0,d)
if e == d:
return shuffle(initial)
ary[d],ary[e]=ary[e],ary[d]
return ary
ary = [1,2,3,4,5,6]
print shuffle(ary)
Also note that Wikipedia gives a (non-recursive) python implementation of the very similar Sattolo's algorithm.
from random import randrange
def sattoloCycle(items):
i = len(items)
while i > 1:
i = i - 1
j = randrange(i) # 0 <= j <= i-1
items[j], items[i] = items[i], items[j]
return
If I read the article correctly, to re-acquire Fisher-Yates, you'd just do one simple change:
from random import randrange
def FisherYates(items):
i = len(items)
while i > 1:
i = i - 1
j = randrange(i+1) # 0 <= j <= i
items[j], items[i] = items[i], items[j]
return
def function(list):
len(list)-1
for i in range(len(list)-1,0,-1):
e= randint(0,i)
while e > i:
e= randint(0,i)
"do something to the list"
return array
?
def function(list):
for i in (a for a in range(len(list)-1,0,-1) if randint(0,a) > a):
#do something with list
#do something else with remainder.
Not exactly what you asked for. Just wanted to remind you of this possibility.
you can copy the parameter to a temp variable. then call the function with the temp variable and use return;
def function(list):
listCopy = list;
len(list)-1
for i in range(len(list)-1,0,-1):
e= randint(0,i)
if e > i:
return function(listCopy)
else
"do something with the list"
return array

Categories