How to deal with this exercise using recursion in Python? - python

I am struggling to solve an exercise regarding lists in Python. The exercise says:
Write a function that takes a list containing numbers and lists of
numbers inside them, reverses it (including the lists of numbers
inside the main list) using recursion.
Afterwards, the function should return a tuple containing three
elements. The first element represents the amount of all the numbers
present in the list (including the ones in the sublists), the second
element is the sum of all the numbers (also the ones inside the
sublists), and the third element is a sorted list of all the integers.
Again we should use recursion.
I managed to reverse the whole list using two functions, but when it comes to accessing the numbers inside the sublists to use them for the tuple, I really don't know what to do.
Here you can have a look at the list of lists:
lista = [3, 3, 5, [[1, 8, [9, 3]], 3, [2, [9, [5, 6],[9]] ] ]]
Here you can check my code:
def exercise (lista):
lista_ordinata = []
count = 0
somma = 0
reverse_list(lista)
for x,y in enumerate(lista):
if isinstance (x,(int)):
count += 1
else:
count = 0
for num in lista:
if isinstance(num,(int)):
somma += num
for i in lista:
if isinstance(i,int):
lista_ordinata.append(i)
return (count,somma,lista_ordinata)
def is_list (lista):
return isinstance(lista,list)
def reverse_list(lista):
lista_nuova = lista[::-1]
for x,y in enumerate(lista_nuova):
if is_list(y):
lista_nuova[x] = reverse_list(y)
lista.clear()
lista.extend(lista_nuova)
return lista
Here you can see the expected list which I reversed:
lista = [[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
The function must return the following tuple:
(13,66, [1,2,3,5,6,8,9])
The output I get is incorrect:
(4, 11, [5, 3, 3])
The first element should be the counting of all the numbers, and not just the numbers outside the sublists, The sum is also incorrect, The list is not outputting all the numbers.
What should I do? Keep in mind that the "exercise" function should use recursion.

You are using recursion just fine in the reverse function, just use recursion in you exercise function:
def exercise(lista):
lista_ordinata = []
count = 0
somma = 0
# reverse_list(lista)
for x in lista:
if isinstance(x, list):
recursion = exercise(x) # call recursion until you have a list with only integers
# and add the result to your running totals
count += recursion[0]
somma += recursion[1]
lista_ordinata.extend(recursion[2])
else:
count += 1
somma += x
lista_ordinata.append(x)
lista_ordinata = sorted(list(set(lista_ordinata)))
return count, somma, lista_ordinata
print(exercise(lista))
(13, 66, [1, 2, 3, 5, 6, 8, 9])

def exercise(a, polish=sorted):
if not isinstance(a, list):
return 1, a, {a}
a.reverse()
amount, sum, numbers = 0, 0, set()
for b in a:
a, s, n = exercise(b, set)
amount += a
sum += s
numbers |= n
return amount, sum, polish(numbers)
Or with a little helper doing the reversals and collecting the numbers:
def exercise(a):
def go(a):
if isinstance(a, list):
a.reverse()
for b in a:
go(b)
else:
numbers.append(a)
numbers = []
go(a)
return len(numbers), sum(numbers), sorted(set(numbers))
Demo:
lista = [3, 3, 5, [[1, 8, [9, 3]], 3, [2, [9, [5, 6],[9]] ] ]]
>>> exercise(lista)
(13, 66, [1, 2, 3, 5, 6, 8, 9])
>>> lista
[[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]

Here's a way without any loops at all.
First of all, we can have a definition for a partial insertion sort, which inserts any value to its correct position in an existing sorted array.
def partialInsertionSort(val, idx, sortarr):
if idx == len(sortarr):
sortarr.append(val)
elif val < sortarr[idx]:
sortarr.insert(idx, val)
elif val > sortarr[idx]:
sortarr = partialInsertionSort(val, idx+1, sortarr)
return sortarr
As you travel through any list a, unless you've reached the end of a, there are two possibilities:
The current item is another list -> Recurse through the sublist!
The current item is a digit -> Increment total and count. If this digit is a previously unseen digit, 'insert' it to your list of sorted digits.
Done with the above two possible options, you have thus processed the current element, and can move to the next element by incrementing the index i.
def reverse_and_sort(a, i, out, sortarr, total, count):
if i==len(a):
return out, sortarr, total, count
if isinstance(a[i], list):
a[i], sortarr, total, count = reverse_and_sort(a[i], 0, [], sortarr, total, count)
else:
total += a[i]
count += 1
if a[i] not in sortarr:
sortarr = partialInsertionSort(a[i], 0, sortarr)
out.insert(0, a[i])
return reverse_and_sort(a, i+1, out, sortarr, total, count)
Test:
rev, srt, total, count = reverse_and_sort(lista, 0, [], [], 0, 0)
print(rev) #[[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
print(srt) #[1, 2, 3, 5, 6, 8, 9]
print(total) #66
print(count) #13

A relatively short version, which uses the add operator is given by that:
from operator import add
def sorter(l):
if isinstance(l, list):
temp = [0, 0, []]
for e in map(sorter, l):
temp = list(map(add, e, temp))
return temp[0], temp[1], sorted(set(temp[2]))
return 1, l, [l]
It should give the correct result:
test_list = [[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
sorter(test_list)
>>> (13, 66, [1, 2, 3, 5, 6, 8, 9])

Two liner approach!
Here is a simple solution to the problem, using indirect recursion. Indirect recursion is where a function f(x) calls a function g(x) which in turn calls f(x).
If your problem was just the first part, the solution is just a simple 2 liner -
f = lambda x: [g(i) for i in reversed(x)]
g = lambda x: f(x) if type(x)==list else x
f(lista)
[[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
Beautiful right?
The main idea here is that at an element level you just need to check if the element is an integer and return it, else you need to use another function that lets you iterate over the list. You can have counters at the level of the second function which lets you track the sum and the count and return it with the final function call.
Since you also have the 2nd part to the solution which is using a few counters, you can modify the above code for g(x) to incorporate that as below -
cnt = []
sm = []
#function to iterate over a reversed list
f = lambda x: [g(i) for i in reversed(x)]
#function to call f if list else updated counters and return element
def g(x):
if type(x)==list:
return f(x)
else:
cnt.append(1) #ONLY MODIFICATION
sm.append(x) #ONLY MODIFICATION
return x
#call f and return sum of counters
def rec(l):
o = f(l)
return o, (sum(cnt), sum(sm), sorted(sm))
out, tup = rec(lista)
print(out)
print(tup)
[[[[[9], [6, 5], 9], 2], 3, [[3, 9], 8, 1]], 5, 3, 3]
(13, 66, [1, 2, 3, 3, 3, 3, 5, 5, 6, 8, 9, 9, 9])

def exercise(lista, total = 0, count = 0, all_int = []):
for item in lista:
if isinstance(item, list):
count, total, all_int = exercise(item, total, count, all_int)
elif isinstance(item, int):
total += item
count += 1
if item not in all_int:
all_int.append(item)
all_int.sort()
lista.reverse()
return count, total, all_int
Maybe this would help :)

def exercise (lista):
reverse_list(lista)
r = count_numbers(lista), sum_numbers(lista), sorted(pull_out(lista))
return r
def is_list (lista):
return isinstance(lista,list)
def reverse_list(lista):
lista_nuova = []
for e in lista[::-1]:
if is_list(e):
lista_nuova.append(reverse_list(e))
else:
lista_nuova.append(e)
return lista_nuova
def count_numbers(lista):
c = 0
for e in lista:
if is_list(e):
c += count_numbers(e)
else:
c+=1
return c
def sum_numbers(lista):
s = 0
for e in lista:
if is_list(e):
s += sum_numbers(e)
else:
s+=e
return s
def pull_out(lista):
b_lista = []
for e in lista:
if is_list(e):
b_lista.extend(pull_out(e))
else:
b_lista.append(e)
return b_lista
lista = [3, 3, 5, [[1, 8, [9, 3]], 3, [2, [9, [5, 6],[9]] ] ]]
r = exercise(lista)
print(r)
# (13, 66, [1, 2, 3, 3, 3, 3, 5, 5, 6, 8, 9, 9, 9])
I did it with separate functions for clarity.

Related

How to stop my loop from ending at the end of i

The prompt is:
Using PyCharm, under lab8, under Directory exercise, create a Python file called
more_loops.py. Write a function, nbrs_greater that accepts 2 integer lists as parameters,
and returns a list of integers that indicates how many integers in the second list are greater than each integer in the first list. For example: nbrs_greater([3, 4, 1, 2, 7], [10, 1, 4, 2, 5, 3]) returns [3, 2, 5, 4, 1]
And my code is working sometimes, but when I enter:
nbrs_greater([20, 5, 1, 6], [1, 4, 8, 12, 16])
It won't continue past 6 and returns [0, 3, 4] instead of [0, 3, 4, 3] because there are three values for which 6 is greater than in list 2.
Here is my original code--------
def nbrs_greater(list_1, list_2):
final_list = []
list_count = []
list_greater = []
number_of_greater = 0
for i in list_1:
for j in list_2:
if i < j:
list_greater.append(i)
number_of_greater += 1
if i > max(list_2):
list_count.append(0)
for k in list_greater:
count_list_var = list_greater.count(k)
list_count.append(count_list_var)
for h in list_count:
if h not in final_list:
final_list.append(h)
if len(final_list) == 0:
final_list.append(0)
return final_list
print(nbrs_greater([20, 5, 1, 6], [1, 4, 8, 12, 16]))
You're making this much more complicated than it needs to be. You don't need a list of each number that's greater in order to get the count. You can use the sum() function to get a count.
def nbrs_greater(list_1, list_2):
final_list = []
for i in list_1:
greater_count = sum(j > i for j in list_2)
final_list.append(greater_count)
return final_list
At here if h not in final_list: you mean final_list has not same num,so it won't apear two '3'.The different count numbers can be ok.

Return an array of the SUM of two arrays

I was recently asked this in my 1st coding interview and it has still got me stumped...
The task is:
Return an array that is equal to the sum of two arrays
Arrays may be blank
Arrays may be different sizes
Items in array will always be type(int)
You cannot use a library or numpy or any built in functions
Example input with expected output:
add_two_array([1, 2, 3], [4, 5, 6]) => [5, 7, 9]
add_two_array([9, 8, 7], [6, 5, 4]) ==> [1, 6, 4, 1]
I don't really understand the requirement 'cannot use any built in functions'. What's the point in using the language. This only uses the list type to append and reverse (slice):
def add_two_array(arr1, arr2):
def arr_to_int(arr):
n = e = 0
for d in arr[::-1]:
n += d*10**e
e += 1 # would be easier to use enumerate
return n # return sum(d*10**e for e, d in enumerate(arr[::-1])
total = arr_to_int(arr1) + arr_to_int(arr2)
result = []
while total:
result.append(total%10)
total //= 10
return result[::-1]
In []:
add_two_array([1, 2, 3], [4, 5, 6])
Out[]:
[5, 7, 9]
In []:
add_two_array([9, 8, 7], [6, 5, 4])
Out[]:
[1, 6, 4, 1]
An alternative (slightly uglier) way would be to use a reverse index and carry a remainder. Uses the ternary operator to set a default value of 0:
def add_two_array(arr1, arr2):
result = []
rem = 0
for i in range(max(len(arr1), len(arr2))-1, -1, -1):
n = (arr1[i] if i < len(arr1) else 0) + (arr2[i] if i < len(arr2) else 0) + rem
result.append(n%10)
rem = n//10
if rem:
result.append(rem)
return result[::-1]
In []:
add_two_array([9, 8, 7], [6, 5, 4])
Out[]:
[1, 6, 4, 1]
your problem statement is missing the indication that you actually want to add digits and carry over the extra ten:
def add_two_array(A,B,carry=0):
if not (A or B or carry): return []
carry += A[-1] if A else 0
carry += B[-1] if B else 0
return add_two_array(A[:-1],B[:-1],carry//10)+[carry%10]
r = add_two_array([1, 2, 3], [4, 5, 6]);
print(r) # [5, 7, 9]
r = add_two_array([9, 8, 7], [6, 5, 4]);
print(r) # [1, 6, 4, 1]
This is a cool problem and can be solved recursively without using any built-in functions and methods whatsoever. Here, _add2 and _add3 are the recursive helper functions and add is the wrapper.
def _add2(a, carry):
if a == []: return [carry] if carry else []
s = a[-1] + carry
digit = s % 10
carry = s // 10
return _add2(a[:-1], carry) + [digit]
def _add3(a, b, carry):
if a == []: return _add2(b, carry)
if b == []: return _add2(a, carry)
s = a[-1] + b[-1] + carry
digit = s % 10
carry = s // 10
return _add3(a[:-1], b[:-1], carry) + [digit]
def add(a, b):
return _add3(a, b, 0)
add([9, 8, 7], [1, 6, 5, 4])
#[2, 6, 4, 1]
add([1, 6, 5, 4], [9, 8, 7])
#[2, 6, 4, 1]

Recursive function to split a list in python

I have a function I've written to take a list of arbitrary values and to split it at certain values into sublists. Basically, I want to take a list and split it at all occurrences of a specific value, returning a list of sublists. I figured the easiest way to do this would be via a recursive function as below.
def recsplit(L, val):
if L.count(val)==0: # If there are no occurrences, return the whole list
return L
elif L.index(val)==0: # If the value is the first element, return everything else
return recsplit(L[1:],val)
else: # Otherwise, split at the first instance of value
return L[:L.index(val)], recsplit(L[L.index(val)+1:],val)
The function should work like this:
>>> P = [1,2,3,4,5,None,None,6,7,8,None,9,10,11,None]
>>> recsplit(P,None)
[[1,2,3,4,5],[6,7,8],[9,10,11]]
Unfortunately I get the following output:
([1, 2, 3, 4, 5, 6, 7], ([8, 9, 10, 11], ([12, 13, 14, 15], [])))
I'm sure there's a way to handle this, but I have tried as many combinations I can think of and none seem to work for me.
I don't think recursion is the easiest way to do this, when you can use itertools.groupby:
from itertools import groupby
lst = [list(g) for k, g in groupby(P, lambda x: x is not None) if k]
print(lst)
# [[1, 2, 3, 4, 5], [6, 7, 8], [9, 10, 11]]
Also keep in mind that recursion is not cheap.
As someone already pointed out, recursive function might not be the best way (at least in python) for this specific task. But since you asked, here is the code with recursive call to generate the exact output you expected.
def recsplit(L, val, out=[[]]):
if L == []:
return out
elif L[0] == val and out[-1] != [] and L[1:].count(val) != len(L[1:]):
return recsplit(L[1:], val, out + [[]])
elif L[0] != val and L[0] is not None:
return recsplit(L[1:], val, out[:-1] + [out[-1] + [L[0]]])
else:
return recsplit(L[1:], val, out)
P = [1,2,3,4,5,None,None,6,7,8,None,9,10,11,None]
P1 = [1,2,None,3,4,5,None,"x","x",None,6,7,8,"x",9,10,11,None,"x","x"]
print("Subs of P by None =", recsplit(P,None))
print("Subs of P1 by x =", recsplit(P1,"x"))
print("Subs of P1 by None =", recsplit(P1,None))
==>
Subs of P by None = [[1, 2, 3, 4, 5], [6, 7, 8], [9, 10, 11]]
Subs of P1 by x = [[1, 2, 3, 4, 5], [6, 7, 8], [9, 10, 11]]
Subs of P1 by None = [[1, 2], [3, 4, 5], ['x', 'x'], [6, 7, 8, 'x', 9, 10, 11], ['x', 'x']]

function to sort a list of positive numbers to return odd numbers first and even numbers last

i want to write a function that takes in a list of numbers (positive integers) and returns a list of sorted numbers such that odd numbers come first and even numbers come last
For example:
my_sort([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) => [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
my_sort([1, 2]) => [1, 2]
my_sort([2, 1]) => [1, 2]
my_sort([3, 3, 4]) => [3, 3, 4]
my_sort([90, 45, 66]) => [45, 66, 90]'''
This is my code
def my_sort(numbers):
a = [n for n in numbers if n % 2 != 0]
b = [n for n in numbers if n % 2 == 0]
new_num = b + a
for m in numbers:
if a and b:
return new_num
else:
return "Invalid sorted output"
Which fails all the test. I'm new to programming and python. So I'ld appreciate if anyone could help me with this.
And here is the unittest
import unittest
class MySortTestCases(unittest.TestCase):
def setUp(self):
self.result1 = my_sort([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
self.result2 = my_sort([1, 2])
self.result3 = my_sort([2, 1])
self.result4 = my_sort([3, 3, 4])
self.result5 = my_sort([90, 45, 66])
def test_output_1(self):
self.assertEqual(self.result1, [1, 3, 5, 7, 9, 2, 4, 6, 8, 10],
msg='Invalid sorted output')
def test_output_2(self):
self.assertEqual(self.result2, [1, 2], msg='Invalid sorted
output')
def test_output_3(self):
self.assertEqual(self.result3, [1, 2], msg='Invalid sorted
output')
def test_output_4(self):
self.assertEqual(self.result4, [3, 3, 4], msg='Invalid sorted
output')
def test_output_5(self):
self.assertEqual(self.result5, [45, 66, 90], msg='Invalid
sorted output')
You could so it by splitting the list into odd and even and then sorting both and concatenating the two lists.
def my_sort(numbers):
odd = [n for n in numbers if n % 2 != 0]
even = [n for n in numbers if n % 2 == 0]
return sorted(odd) + sorted(even)
See that this
>>> my_sort([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
[1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
But using a key function avoids constructing the split lists:
>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> numbers.sort(key=lambda v: (v%2==0, v))
>>> numbers
[1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
This sorts the list using a key function that returns a tuple of (0, v) if v is odd and (1, v) if even. This causes the odd numbers to appear before the even numbers in an ascending ordered sort.
It can be turned into a function:
def my_sort(numbers):
return sorted(numbers, key=lambda v: (v%2==0, v))
So, I went ahead and grabbed your code and did just a few tests by myself to see the actual output. Your code doesn't actually sort the list currently. It only puts the even numbers first and the odd numbers second. Which by the sound of it isn't what you want either.
>>> my_sort([4, 5, 7, 1, 2, 6, 3])
[4, 2, 6, 5, 7, 1, 3]
>>> my_sort([1, 2])
[2, 1]
These were the outputs I got by running your code using the Python interpreter. As you can see the even numbers are first, unsorted, followed by the odd numbers, also unsorted. This just has to do with the way you made the new_num list. You have new_num = b + a, but you created b by looking for all the even numbers b = [n for n in numbers if n % 2 == 0] and created a by looking for all the odd numbers a = [n for n in numbers if n % 2 != 0]. The % returns the remainder. So, if a number is divisible by 2 it returns 0 and this means that it is even. So, you can either flip the assignment of a and b or you can flip when you add them together so a is first and b is second.
As for the individual chunks not being sorted. Python has a built in sorted function that you can call on list sorted(my_list) that returns a sorted version of that list. So, if you just run that on your a and b when you're adding them together to create your new_num list then the numbers for each should be sorted just fine.
Your if statements at the end inside your for loop are also not working properly. Giving an already sorted list just returns the list given. Your code here:
for m in numbers:
if a and b:
return new_num
else:
return "Invalid sorted output"
This is looping through the original list given and returns the new_num if a and b exists. Since a and b will always exist this will always return new_num and never the "Invalid sorted output" statement. You need to make sure you're checking to see if the numbers list is already sorted with the expected output of your function. My suggestion would be check to see if new_num is the same as numbers.
Just use sorted twice. First to sort the list, and again to sort it by odd then even using the key parameter.
x = [3,5,4,1,6,8,10,2,9,7]
sorted(sorted(x), key=lambda x: (x+1)%2)
# returns:
# [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
This should work... It makes sense if you are joining Andela.
def my_sort(theNumbers):
#pass
evenNumbers=[]
oddNumbers=[]
#CHECK IF EVEN OR ODD THEN STORE THEN SEPARATE
for i in theNumbers:
if i%2==0:
evenNumbers.append(i)
else:
oddNumbers.append(i)
#print (evenNumbers)
#SORT DEM LISTS
evenNumbers.sort()
oddNumbers.sort()
#print (evenNumbers)
#join the two
oddNumbers+=evenNumbers
print (oddNumbers)
#my_sort([11111, 1, 11, 979749, 1111, 1111])
This code will sort even and odd numbers without creating a temporary list.
def segregateEvenOdd(arr, index=0, iterations=0):
if iterations == len(arr):
return arr
if arr[index]%2 == 0:
arr.append(arr[index])
arr.pop(index)
return segregateEvenOdd(arr, index, iterations+1 )
if arr[index]%2 != 0:
return segregateEvenOdd(arr,index+1,iterations+1)
arr = [ 2, 3, 9, 45, 2, 5, 10, 47 ]
output = segregateEvenOdd(arr)
print(output)
# Output
# [3, 9, 45, 5, 47, 2, 2, 10]
This code should work:
def sort_integers(list_of_integers):
odd_numbers = [n for n in list_of_integers if n%2!=0]
odd_numbers = sorted(odd_numbers, reverse = True)
print(odd_numbers)
even_numbers = [x for x in list_of_integers if x%2 == 0]
even_numbers = sorted(even_numbers, reverse = True)
print(even_numbers)
new_sorted_list = even_numbers + odd_numbers
print(new_sorted_list)

Sum of nested list without using SUM function (exercise)

Trying to write a function that take the sum of each list and return individual values in a new single list.
E.g
[[2, 7, 6], [9, 5, 1], [4, 3, 8]]
becomes
[15, 15, 15]
What I have so far:
def row_sums(square):
total_list = []
total = 0
for i in square:
for j in i:
total += j
total_list.append(total)
return total_list
But this just accumulates each list onto each other resulting in:
[15, 30, 45]
I'm not sure how to keep the sums for each list separate here. The SUM function is not allowed here as it's an exercise on nested loops.
Thanks.
You need to reset your total counter before starting each inside for.
Also, you don't need to declare it outside, because you will use it only inside.
def row_sums(square):
total_list = []
for i in square:
total = 0
for j in i:
total += j
total_list.append(total)
return total_list
The error is you do not re-initialize the total variable after each loop. Instead, initialize sum = 0 inside he first for-loop like so:
def row_sums(square):
total_list = []
for i in square:
total = 0
for j in i:
total += j
total_list.append(total)
return total_list
Just for fun:
>>> list = [[2, 7, 6], [9, 5, 1], [4, 3, 8]]
>>> import functools
>>> [functools.reduce(lambda x, y: x + y, sublist, 0) for sublist in list]
[15, 15, 15]
I did't use sum :)
You can read more about functools.reduce here.
Edit: As Sevanteri pointed out in the comment, you can also use [functools.reduce(int.__add__, sublist, 0) for sublist in list]
(if you really want to drive your teacher mad!)
You need to zero your total for each list.
def row_sums(square):
total_list = []
total = 0
for i in square:
for j in i:
total += j
total_list.append(total)
total = 0
return total_list
To be different, flatten the lists and using a generator (assumes sublists are the same length):
def _notsum2(lists):
per_yield = len(lists)
total = 0
for ind, next in enumerate(val for sublist in lists for val in sublist):
if ind % per_yield == 0 and ind:
yield total
total = 0
total += next
yield total
if __name__ == '__main__':
li = [[2, 7, 6], [9, 5, 1], [4, 3, 8]]
print [g for g in _notsum2(li)]
You can also do it using map and list comprehension as:
l=[[2, 7, 6], [9, 5, 1], [4, 3, 8]]
a,b,c=[x for x in l]
map(lambda x,y,z:x+y+z, a,b,c)
[sum(i) for i in zip(*[[2, 7, 6], [9, 5, 1], [4, 3, 8]])]
bultin zip func is what you exactly needed

Categories