Recursively determining if a sequence is nondecreasing - python

I want to use recursion here but my code is not fully correct. It is correct for some of the test cases. Help me where I'm wrong. I have to return recursive statement. Basically, I don't want to expand my code.
def nondecreasing(l):
if l==[] or len(l) == 1:
return(True)
else:
return(nondecreasing(l[1:-1]) if (l[1]<=l[2]) else False)
This code should check if the list is non-decreasing or not. A list is a non-decreasing if each element is at least as big as the preceding one. For instance [], [7], [8,8,11] and [3,19,44,44,63,89] are non-decreasing, while [3,18,4] and [23,14,3,14,3,23] are not.
The longer nondecreasing test case fails:
>>> nondecreasing([3,19,44,44,63,89])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in nondecreasing
File "<stdin>", line 5, in nondecreasing
File "<stdin>", line 5, in nondecreasing
IndexError: list index out of range

You are doing two things wrong:
You seem to assume Python indexing is 1-based. It's not, you are ignoring the value of l[0]. This also causes an issue with trying to access l[2]; that index doesn't exist when your list only contains 2 elements.
>>> def nondecreasing(l):
... if l==[] or len(l) == 1:
... return(True)
... else:
... return(nondecreasing(l[1:-1]) if (l[1]<=l[2]) else False)
...
>>> nondecreasing([1, 2])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in nondecreasing
IndexError: list index out of range
You are ignoring the last value of the list when recursing; slicing to [...:-1] removes the last element, causing you to fail to detect a single last decreasing value:
>>> nondecreasing([1, 2, 3, 4, 0])
True
The following code corrects both errors:
def nondecreasing(l):
if len(l) < 2:
return True
return nondecreasing(l[1:]) if l[0] <= l[1] else False
The l[1:] slice copies all elements except the first one.
Personally, I'd probably not use the conditional expression on the last line. The following is a little clearer:
def nondecreasing(l):
if len(l) < 2:
return True
if l[0] > l[1]:
return False
return nondecreasing(l[1:])
Demo:
>>> def nondecreasing(l):
... if len(l) < 2:
... return True
... if l[0] > l[1]:
... return False
... return nondecreasing(l[1:])
...
>>> nondecreasing([])
True
>>> nondecreasing([7])
True
>>> nondecreasing([8, 8, 11])
True
>>> nondecreasing([3, 19, 44, 44, 63, 89])
True
>>> nondecreasing([3, 18, 4])
False
>>> nondecreasing([23, 14, 3, 14, 3, 23])
False

Maybe it should be
def nondecreasing(l):
if len(l) <= 1:
return True
else:
# the l[1] <= l[2] should be l[0] <= l[1], and l[1:-1] should be l[1:]
return nondecreasing(l[1:]) if (l[0] <= l[1]) else False

Related

Writing list with criteria 1 line - Python

I am trying to get better at writing more 'clean' and/or elegant code. I have seen examples around but I can't make them work for this particular example. Logic is very simple and I'm hoping that with some pointers on punctuation or syntax I can pick up the habit.
a = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
elist = []
evenlist = [i for i in a if a % 2 == 0]
print(evenlist)
I have only been able to make this work on the longer format here below:
a = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
elist = []
for i in a:
if i % 2 == 0:
elist.append(i)
print(elist)
Should be this:
evenlist = [i for i in a if i % 2 == 0]
evenlist = [i for i in a if a % 2 == 0]
# ^
# Hmmm!
You probably wat to be checking i (an element) for evenness, rather than a (the entire list). Checking a list for evenness doesn't make much sense unless there's some magic happening in the background that ensures every element in the list is even. But there's not:
>>> [1,2,3] % 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for %: 'list' and 'int'
As an aside, there's another way to do this, specifically:
list(filter(lambda x: x % 2 == 0, a))
I think this is less readable myself but I suspect it may be more space-efficient if you just want to process them rather than create a list from them:
for item in filter(lambda x: x % 2 == 0, a):
do_something_with(item)
That doesn't create a whole new list, instead simply retrieving values from the existing list that match the filter criteria. Probably won't matter unless your lists tend to get large.

variable error in function while assignment

Write a Python script to sort (ascending and descending) a dictionary by value
def sort_dictionary_ascending(dict):
flag = True
list = []
while(len(dict)!=1):
flag = True
for x,y in dict.items():
if flag:
min = x
flag = False
elif min>x:
min =x;
list.append(min)
dict.pop(min)
min,value= dict.popitem()
list.append(min)
print(list)
def sort_dictionary_descending(dict):
flag = True
list = []
while(len(dict)!=1):
flag = True
for x,y in dict.items():
if flag:
max = x
flag = False
elif max < x:
max = x
list.append(max)
dict.pop(max)
max,value= dict.popitem()
list.append(max)
print(list)
d = {1: 1, 3: 3, 4: 4, 2: 2, 5: 5}
sort_dictionary_descending(d)
sort_dictionary_ascending(d)
Error is:
/home/admin2/Desktop/two/venv/bin/python /home/admin2/Desktop/two/sort_dictionary.py
Traceback (most recent call last):
File "/home/admin2/Desktop/two/sort_dictionary.py", line 45, in <module>
sort_dictionary_ascending(d)
File "/home/admin2/Desktop/two/sort_dictionary.py", line 17, in sort_dictionary_ascending
list.append(min)
UnboundLocalError: local variable 'min' referenced before assignment
[5, 4, 3, 2, 1]
First in your for loops use for x in dict.keys() instead of for x, y in dict.items()
because y variable isn't used in the whole loop. Second, I don't understand why you use a dict which the key is equal to the value, a list is more suitable

Why the second half of list is note operated on in a recursive call in merge sort

I am given a task to implement merge sort in python. I have written the code but I am stuck at Why the code doesn't print part_b of the list after a recursive call and how shall I fix it.
Below is the code
def merge_sort(list_sort):
"""splits the list in two parts until each part is left with one member"""
if len(list_sort) == 1:
print len(list_sort)
return list_sort
if len(list_sort)>= 2:
x= len(list_sort) / 2
part_a = list_sort[:x]
part_b = list_sort[x:]
print part_a , part_b
merge_sort(part_a)
merge_sort(part_b)
return merge(part_a, part_b)
def merge(left , right):
"""merges the two parts of list after sorting them"""
print left, right
sorted_list = []
if len(left) >= len(right):
i = len(left)
while i != 0:
if left[i] > right[i]:
sorted_list.append(right[i])
else :
sorted_list.append(left[i])
i = i-1
sorted_list += right[i:]
else :
i = len(right)
while i != 0:
if left[i] > right[i]:
sorted_list.append(right[i])
else :
sorted_list.append(left[i])
i = i-1
sorted_list += left[i:]
return sorted_list
details = [3, 7, 5, 12, 14, 11, 2, 6]
print merge_sort(details)
I believe that your claim is incorrect. I updated your prints to label the output
print "SORT A", part_a , "\tB", part_b
print "MERGE left", left, "\tright", right
and got the expected trace, until it crashed on the index mishandling in the merge phase. As others have noted, this is from failing to save the return value from merge.
SORT A [3, 7, 5, 12] B [14, 11, 2, 6]
SORT A [3, 7] B [5, 12]
SORT A [3] B [7]
1
1
MERGE left [3] right [7]
Traceback (most recent call last):
File "so.py", line 44, in <module>
print merge_sort(details)
File "so.py", line 13, in merge_sort
merge_sort(part_a)
File "so.py", line 13, in merge_sort
merge_sort(part_a)
File "so.py", line 16, in merge_sort
return merge(part_a, part_b)
File "so.py", line 26, in merge
if left[i] > right[i]:
IndexError: list index out of range
UPDATE per OP COMMENT
Part B isn't operated on because of your (valid) call order: the algorithm must finish sorting and merging part A before it works on part B. You don't get that far because of the index range error. You faulted on trying to merge [3] and [7] -- parts AAA and AAB, respectively. You never made it back up the stack (AA and AB) to work on the original part B.
You are trying to use your merge_sort as if it operates in place, but it does not. You need to capture its return value:
part_a = merge_sort(part_a)
part_b = merge_sort(part_b)
Your merge_sort function returns the sorted list but at both locations you call it you discard the result. You need to assign the result (bind it to a name) so that you can pass those results to merge:
if len(list_sort) >= 2:
x= len(list_sort) / 2
part_a = list_sort[:x]
part_b = list_sort[x:]
print part_a , part_b
sorted_part_a = merge_sort(part_a)
sorted_part_b = merge_sort(part_b)
return merge(sorted_part_a, sorted_part_b)

How can you join int and list together?

But I still can't find the join function anywhere else on the internet. The main problem is that the head(items) is int and tail(items) is list, and I can't combine head and tail together. Here is the code I tried:
def head(items):
return items[0]
def tail(items):
return items[1:]
def isEven(x):
return x % 2 == 0
def extractEvens(items):
if (items == None):
return None
elif (isEven(head(items))):
return join(head(items),extractEvens(tail(items)))
else:
return extractEvens(tail(items))
a = [4,2,5,2,7,0,8,3,7]
print(extractEvens(a))
Here is the link for the page I tried to study: The is the code for filter pattern:
link_of_code
You can also try insert which is even more useful as the head must be in the starting.
l = extractEvens(tail(items))
l.insert(0,head(items))
return l
Please, provide an example of the desired output.
If you want to create a new list, merging a list with an int, it should be:
return [head(items)] + tail(items)
what you need is append
>>> a=6
>>> b=[1,2,3,4]
>>> b.append(a)
>>> b
[1, 2, 3, 4, 6]
here you just cant concanate list and int:
>>> a=6
>>> b=[1,2,3,4]
>>> a+b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'list'
>>> list(a)+b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
but if you do str, it will concanated as str not int:
>>> list(str(a))+b
['6', 1, 2, 3, 4]
There are multiple errors in your code:
def isEven(x):
return x % 2 == 0 # x % 2 not x % ==
def extractEvens(items):
if not items:
return [] # return empty list not None
elif isEven(head(items)):
# convert ints to strings and put head(items) in a list
return "".join(map(str,[head(items)]) + map(str,extractEvens(tail(items))))
else:
return extractEvens(tail(items))
You can also do this in a single list comprehension:
a = [4, 2, 5, 2, 7, 0, 8, 3, 7]
print("".join([str(x) for x in a if not x % 2]))

Is safe ( documented behaviour? ) to delete the domain of an iterator in execution

I wanted to know if is safe ( documented behaviour? ) to delete the domain space of an iterator in execution in Python.
Consider the code:
import os
import sys
sampleSpace = [ x*x for x in range( 7 ) ]
print sampleSpace
for dx in sampleSpace:
print str( dx )
if dx == 1:
del sampleSpace[ 1 ]
del sampleSpace[ 3 ]
elif dx == 25:
del sampleSpace[ -1 ]
print sampleSpace
'sampleSpace' is what I call 'the domain space of an iterator' ( if there is a more appropriate word/phrase, lemme know ).
What I am doing is deleting values from it while the iterator 'dx' is running through it.
Here is what I expect from the code :
Iteration versus element being pointed to (*):
0: [*0, 1, 4, 9, 16, 25, 36]
1: [0, *1, 4, 9, 16, 25, 36] ( delete 2nd and 5th element after this iteration )
2: [0, 4, *9, 25, 36]
3: [0, 4, 9, *25, 36] ( delete -1th element after this iteration )
4: [0, 4, 9, 25*] ( as the iterator points to nothing/end of list, the loop terminates )
.. and here is what I get:
[0, 1, 4, 9, 16, 25, 36]
0
1
9
25
[0, 4, 9, 25]
As you can see - what I expect is what I get - which is contrary to the behaviour I have had from other languages in such a scenario.
Hence - I wanted to ask you if there is some rule like "the iterator becomes invalid if you mutate its space during iteration" in Python?
Is it safe ( documented behaviour? ) in Python to do stuff like this?
From the Python tutorial:
It is not safe to modify the sequence
being iterated over in the loop (this
can only happen for mutable sequence
types, such as lists). If you need to
modify the list you are iterating over
(for example, to duplicate selected
items) you must iterate over a copy.
The slice notation makes this
particularly convenient:
>>> for x in a[:]: # make a slice copy of the entire list
... if len(x) > 6: a.insert(0, x)
...
>>> a
['defenestrate', 'cat', 'window', 'defenestrate']
Generally speaking no, it's not safe and you may get unpredictable behaviour. Iterators aren't required to behave in an specific way under these circumstances.
What's happening in your example is
# list is [0, 1, 4, 9, 16, 25, 36]
if dx == 1:
# we're at index 1 when this is true
del sampleSpace[ 1 ]
# we've removed the item at index 1, and the iterator will move to the next valid position - still index 1, but in a mutated list. We got lucky in this case
# the list now contains [0, 4, 9, 16, 25, 36]
del sampleSpace[ 3 ]
# we remove the item at index 3 which is (now) value 16
# the list now contains [0, 4, 9, 25, 36]
elif dx == 25:
del sampleSpace[ -1 ]
# we remove the final item, list now looks like
# the list now contains [0, 4, 9, 25]
What do you mean by safe? Your code happens not to raise any errors, but it is a distinct possibility of course, consider this:
>>> a = range(3)
>>> for i in a:
del a
Traceback (most recent call last):
File "<pyshell#13>", line 2, in <module>
del a
NameError: name 'a' is not defined
>>> a
[0, 1, 2]
>>> for i in a:
del a[i+1]
Traceback (most recent call last):
File "<pyshell#27>", line 2, in <module>
del a[i+1]
IndexError: list assignment index out of range
It is not clear why would you want to do this, but there is no additional rules applicable to iterators. They're acting exactly as any other type would.

Categories