How to build a list recursively given only the length? - python

I am trying to write a recursive function that takes a number n and some value, let's say 'a', and recursively builds a list made up of n 'a's.
So, func(4, 'a') returns ['a','a','a','a'].
My internet searches so far have been surprisingly futile. Most examples of list recursion I have found take additional parameters.
I have tried various versions of the following code:
def func(n, a):
if n == 1:
return [a]
else:
return func(n-1, a).append(a)
I keep getting cannot append to NoneType errors on that last return. So func(n-1,a) is returning None.

list.append only returns None as its purpose is to update the target list in-place. Instead of appending, simply add:
def func(n, a):
if n == 1:
return [a]
else:
return func(n-1, a) + [a]
print(func(4, 'a'))
Output:
['a', 'a', 'a', 'a']
Additionally, you may want to consider using generators to provide a cleaner (and shorter) result:
def func(n, a):
if n:
yield a
yield from func(n-1, a)
print(list(func(4, 'a')))
Output:
['a', 'a', 'a', 'a']

Because the list.append method returns None you have to do it this way if you want to use the append method:
def func(n, a):
if n == 1:
return [a]
else:
temp = func(n-1, a)
temp.append(a)
return temp
print(func(5, 'a'))
Prints:
['a', 'a', 'a', 'a', 'a']

Does it have to be recursive?
def func(n, a):
a_list = []
for i in range(n):
a_list.append(str(a))
return a_list

Related

if a function can be created based on different input information?

I have the following question, I am wondering if there is a solution or not.
Because I just learned the function can be a variable in python, I am wondering if it is possible to design a function which create different function based on the input.
let's say, we have a long list of chars:
longlist = abcdefghijklmnopqrstuvwxyz
Given the an test list, test = [1,2,3]
An function (func1) can read the test list as input and return a function (func2) as output.
This function can be used to separate the long list of chars into different group and print out
a,bc,def,g,hi,jkl,o,pq.... which follow the test list 1,2,3 pattern and repeat again.
if the test list is test = [1,2,3,4]
then func1(test) --> func2
func2(longlist) prints out a,bc,def,ghij,k,lm,n
In this case, it follows the 1,2,3,4,1,2... pattern
The example I made looks not so interesting, but the fundamental question is if the function can be created based on different input information?
Yes. This is called a closure. The inner function (func2) keeps the scope that it was defined in. Try this:
def func1(x):
def func2(y):
ret = []
for f in x * len(y):
ret += [y[:f]]
y = y[f:]
if not y:
return ret
return func2
print(func1([1, 2, 3, 4])('This should do what you want'))
You can define a function within the first function and return it later. Function 1 can be used to set up params, etc. Below is an implementation of your particular question.
def make_func(params):
# Params must be a list of integers
def split_string_in_pattern(string):
res = []
pattern_index = 0
while True:
res.append(string[:params[pattern_index]])
print(res)
string = string[params[pattern_index]:]
print(string)
if not string:
return res
if pattern_index >= len(params) - 1:
pattern_index = 0
else:
pattern_index += 1
return split_string_in_pattern
""" Test """
long_string = 'asdqweasdasdacasdasdadas'
split_func = make_func([1,2,3,4])
split_func(long_string)
from itertools import permutations, combinations
# Here you can use which one is more suited for your situation: permutations or combinations
def func1(test):
def func2(longlist):
result = []
for t in test:
perms = permutations(longlist, t)
result += perms
result = [''.join(t) for t in result]
return result
return func2
f2 = func1([1, 2])
print(f2('abc'))
You get
['a', 'b', 'c', 'ab', 'ac', 'ba', 'bc', 'ca', 'cb'] if you used permutations
['a', 'b', 'c', 'ab', 'ac', 'bc'] if you used combinations

A basic filter is returning an empty list

def only_upper(t):
res = []
for s in t:
if s.isupper():
res.append(s)
return res
t = ['a', 'B', 'c', 'D']
print only_upper(t)
I'm trying to figure out why this is returning an empty list. The result looks like this in the console:
[]
You return immediately after the first element of the for loop, instead of after it complete. Unindent your return statement and you should be fine:
def only_upper(t):
res = []
for s in t:
if s.isupper():
res.append(s)
return res # Here!
Also worth mentioning that it's often easier to write filters like this in list-comprehensions instead of implementing the entire logic:
print([x for x in t if x.isupper()])
As alternative to the above list comprehension is built-in filter function (in case it is even further in your book):
list(filter(lambda x: x.isupper(), ['a', 'B', 'c', 'D']))

Assigning a list during recursive function in Python

I need help with assigning lists in a recursive function. How can I assign a value to the ith member of a list when the recursive function is in ith iteration?
You can't change an iterable while using it to drive an iteration, but you could easily make an iterable to hold your results and pass it in to the recursive function as an argument:
results = []
def recurse(level, results):
level += 1
results.append(level)
if level < 10: recurse(level, results)
print recurse(0, results)
>>> [1,2,3,4,5,6,7,8,9,10]
but in this example you could not do
for item in results:
recurse (item, results)
I don't know if I'm understanding well your question, but I think it's something like:
# put "s" on 2nd position of the list ['l', 'u', 'c', 'a', 's']
# so the result should be ['l', 's', 'c', 'a', 's']
If so, keep in mind that there are better ways to do it in Python. So here's the code:
def assign_in_position(elem, my_list, pos):
first = my_list[0]
tail = my_list[1:]
if pos == 0:
return [elem] + tail
return [first] + assign_in_position(elem, tail, pos-1)
If you're new in Python there's only one thing that can be strange (slicing, my_list[1:]). This function is also kind of buggy, but it's proposital. It works for my first example, but if you're learning recursion, then you should learn how to detect and solve common recursion problems.
Out of curiosity, this is the Haskell code for the same task:
assign_in_pos elem [] _ = [elem]
assign_in_pos elem (_:t) 0 = elem:t
assign_in_pos elem (h:t) pos = h:assign_in_pos elem t (pos-1)

Finding the order number of an element in a list

I'm trying to find the order number an item in list for example:
lst = [ a, b, [c,d], e, f]
order([c,d]) = 2
order('e') = 3
I think of this way :
def order(item,lst):
if lst[0] == item:
return n
else:
return order(item,lst[0:])
But it gives error (related recursion depth). What is my fault? Or how can i do it?
Why not just use .index()?
In [1]: l = [ a, b, [c,d], e, f]
In [2]: l.index([c,d])
Out[2]: 2
In [4]: l.index(e)
Out[4]: 3
If you really need a recursive function, use the following:
def order(item, l, n=0):
if l:
if l[0] == item:
return n
elif len(l) >= 2: # for python 2, use "else:"
return order(item, l[1:], n+1)
And if recursion is not a must but you can't use .index(), use a for loop:
def order(item, l):
for i,v in enumrate(l):
if v == item:
return i
With both methods, just call order([c,d], lst)
Your function returns n in the base case, yet never assigns anything to it. If the thing you are looking for is in the first element, it should return 0.
Your recursive case passes the whole list, which is why the recursive never ends. If you passed lst[1:], then lists would get smaller with each call, but you'd need to add 1 to the result (in effect, everything is shifted down 1 place in each recursive call).
def order(item, lst,n=0):
if not lst:
return None
elif lst[0] == item:
return n
else:
return order(item, lst[1:],n+1)
lst = ['a', 'b', ['c', 'd'], 'e', 'f']
order(['c', 'd'], lst)
out:
2
Python has a builtin function to do this:
lst = ['a', 'b', ['c', 'd'], 'e', 'f']
assert lst.index(['c', 'd']) == 2
assert lst.index('e') == 3
If you want to fix your own function, you need a base case:
def order(item, lst):
if not lst:
return None
elif lst[0] == item:
return n # You need to calculate n here.
# I'm not doing your homework for you
else:
return order(item, lst[1:])

how to split a list in two at the point where predicate is first False

I keep thinking there should be a function for this, but I've searched the likely places (google, itertools docs, list methods, other SO questions), but nowhere found quite what I was looking for.
Naive and working implementation:
def split_at_first_false(pred, seq):
first = []
second = []
true_so_far = True
for item in seq:
if true_so_far and pred(item):
first.append(item)
else:
true_so_far = False
second.append(item)
return first, second
print split_at_first_false(str.isalpha, "abc1a2b")
# (['a', 'b', 'c'], ['1', 'a', '2', 'b'])
It works, but it doesn't feel right. There should be a better way to do this!
EDIT: I ended up with using a slightly modified version of senderle's final suggestion after reviewing the answers:
from itertools import chain
def split_at_pred(pred, seq):
head = []
it = iter(seq)
for i in it:
if not pred(i):
head.append(i)
else:
return iter(head), chain([i], it)
return iter(head), iter([])
It's short and elegant, output is two iterators no matter the input (strings, lists, iterators), and as a bonus, it even works with the following input:
from itertools import count
split_at_pred(lambda x: x == 5, count())
The other solutions, those that work at all with iterators, will run out of memory with this input. (Note that this is just a bonus. Infinite iterators was something I hadn't even considered when I wrote this question)
This seems like a job for itertools.
>>> first = list(itertools.takewhile(str.isalpha, l))
>>> second = list(itertools.dropwhile(str.isalpha, l))
>>> first
['a', 'b', 'c']
>>> second
['1', 'a', '2', 'b']
This needs to be altered if l is an iterator rather than a sequence.
>>> def bisect_iter(pred, i):
... i1, i2 = itertools.tee(i)
... return itertools.takewhile(pred, i1), itertools.dropwhile(pred, i2)
...
>>> i1, i2 = bisect_iter(str.isalpha, iter(l))
>>> list(i1)
['a', 'b', 'c']
>>> list(i2)
['1', 'a', '2', 'b']
The downside of tee is that the initial values are cached and tested twice (by both takewhile and dropwhile). That's wasteful. But caching values is unavoidable if you want to both accept and return iterators.
However, if you can return lists from an iterator, I can think of one solution that doesn't make extra copies or tests, and it's very close to yours:
>>> def bisect_iter_to_list(pred, it):
... l1 = []
... for i in it:
... if pred(i):
... l1.append(i)
... else:
... l2 = [i]
... l2.extend(it)
... return l1, l2
...
>>> bisect_iter_to_list(str.isalpha, iter(l))
(['a', 'b', 'c'], ['1', 'a', '2', 'b'])
The only sneaky bit is that where there would normally be a break statement (i.e. after the else clause), I've simply consumed the iterator, causing the for loop to terminate early.
Finally, if you still want to return iterators, but don't want to do extra tests, here's a variation on the above that I believe is optimal.
>>> def bisect_any_to_iter(pred, it):
... it = iter(it)
... head = []
... for i in it:
... if pred(i):
... head.append(i)
... else:
... tail = itertools.chain([i], it)
... break
... return iter(head), tail
...
>>> a, b = bisect_iter_to_iter(str.isalpha, iter(l))
>>> list(a)
['a', 'b', 'c']
>>> list(b)
['1', 'a', '2', 'b']
How about this?
def split_at_first_false(pred, seq):
for i, item in enumerate(seq):
if not pred(item):
return seq[:i], seq[i:]
What about this?
def split_at_first_false(pred, seq):
pos = 0
for item in seq:
if not pred(item):
return seq[:pos], seq[pos:]
pos += 1
Don't shy away from iterators, this is a perfect case for using one. Once the first false item is hit, use the same iterator to just fill out the rest of the items into the second list.
def split_at_false(pred, seq):
# if seq is not already an iterator, make it one
if not hasattr(seq,'next'):
seq = iter(seq)
first, second = [], []
for item in seq:
if not pred(item):
second.append(item)
break
first.append(item)
# at this point, seq points to the first item
# after the false item, just add it and all the
# rest to the second list
second.extend(seq)
return first, second
is_odd = lambda x : x % 2
print split_at_false(is_odd, [1])
print split_at_false(is_odd, [1,2,3,4,5])
print split_at_false(is_odd, [2,3,4,5,6])
print split_at_false(is_odd, [])
Prints:
([1], [])
([1], [2, 3, 4, 5])
([], [2, 3, 4, 5, 6])
([], [])
No tee'ing, no extra list storage, no iterating twice over the list, no slicing, just an iterator.
Try that:
def split_at_first_false(pred, seq):
index = 0
while index < len(seq):
if not pred(seq[index]):
return seq[:index], seq[index+1:]
index+=1
Try the following code :
data = "abc1a2b"
def split_at_first_false(pred, seq):
if not isinstance(seq, list):
seq = list(seq)
for i,x in enumerate(seq):
if not pred(x):
return seq[:i], seq[i:]
return seq, []

Categories