Is there a way to return true for an empty list in a sort invariant? - python-3.x

So I am supposed to write a function that returns true if the said sequence is arranged from smallest to largest. I am able to understand strings and num lists but for an empty list, I am unable to understand why it won't return as true.
This is for understanding and possibly help me understand how to manipulate my loops better.
def is_sorted(seq):
for i in range(0, len(seq)):
if seq[i]<seq[i+1]:
return True
else:
return False
print(is_sorted([])) # expected to be true but returns none

The obvious problem is that with an empty list you are returning None which evaluates to false. The other problem is that you are using return inside of a loop, which means that you aren't evaluating every sequence in the iterable.
#hiro protagonist's answer is one solution to this problem. I offer my alternative using all and a generator expression.
def is_sorted(seq):
return all(seq[i] < seq[i + 1] for i in range(len(seq) - 1))
# All evaluate True
print(is_sorted(['a', 'b', 'c']))
print(is_sorted([1, 2, 3]))
print(is_sorted([]))
# Both evaluate False
print(is_sorted(['a', 'c', 'b']))
print(is_sorted([0, 1, -1]))
Edit with explanation
As best I understand it, all works by stepping through an iterable and returns False if any value in it evaluates to False, otherwise returning True.
As the comments may show you, I don't have a good understanding of Python generators. A generator is an iterable object that calculates the next value and yields it back each time it is referenced.
The generator defined above, each time that all references it, calculates seq[i] < seq[i + 1] and gives that value back. If this is False at any time then all will return False.
I hope this helps. I'm sure one of the good people in the comments will correct any flawed understanding that I have.

Your implementation is wrong. It will return True for [1, 3, 2] since it only compares the first 2 elements (return returns after the first iteration).
It can be fixed by checking for the opposite condition, then return True after the loop.
You should also iterate until len(seq) - 1 otherwise the last iteration will cause an IndexError.
def is_sorted(seq):
for i in range(0, len(seq) - 1):
if seq[i] > seq[i + 1]:
return False
return True
print(is_sorted([1, 2, 3]))
# True
print(is_sorted([1, 3, 2]))
# False
print(is_sorted([]))
# True
And of course there is the trivial, naive solution,
def is_sorted(seq):
return seq == sorted(seq)

this is a variant that also works for empty lists and lists of length 1:
from itertools import islice
def is_sorted(seq):
return all(i <= j for i, j in zip(seq, islice(seq, 1, None)))
it iterates over seq[k] and seq[k+1] using zip and islice. and only if all elements satisfy the requirement True will be returned.

Its because for an empty list the code inside the for is not reached. So neither return statement isnt reached. Also you should take into account that a list with only one element should also return True. Solution:
def is_sorted(seq):
for i in range(0, len(seq)-1):
if seq[i]>=seq[i+1]:
return False
return True

Related

cant break out of Python While loop

I'm quite new to Python so sorry in advance, I'm trying to break out of a While loop if a random number is not within a List of numbers.
Despite testing the output of the functions and confirming that an integer is in the List and it is in fact an integer and other methods return both True and False the While Statement ignores the value. See demo code.
import random,time
list=[i for i in range(10)]
print(list)
print(list[6]*10) # this returns an integer
if list[6]==12/2:
print('this evaluates as a int')
this=99 # sentry to run the while loop **** but cant index by
if 10/2 in list:
print('this also evaluates as an integer')
print(type(list))
print(type(this))
while this not in list:
this = random.randrange(9)
print(this,this in list)
time.sleep(.200)
list[this] = '*'
>>>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
60
this evaluates as a int
this also evaluates as an integer
<class 'list'>
<class 'int'>
6 True
3 True
4 True
5 True
5 False
Its obvious there's a flaw in the While function where it is unable see True or False correctly, thank you to those who proposed suggestions but i come up with my own.
I created a sentry boolean to pass the value to the While function
sentry=True
while sentry:
this = random.randrange(9)
print(this,this in list)
if this not in list:
sentry=False
time.sleep(.200)
list[this] = '*'
The while loop condition should evaluate to False when you want to break out of the loop. In your case, since you want to exit when this is not in list, then set your condition to this in list.
Here is your code with the fixed condition, keep in mind that since the list is directly modified from inside the loop, the loop will terminate after one iteration.
import random, time
list=[i for i in range(10)]
print(list)
this = random.randrange(9)
while this in list:
this = random.randrange(9)
print(this, this in list)
time.sleep(.200)
list[this] = '*'
print(list)
If you did not want the loop to terminate after one iteration, you may want to consider relocating the expression list[this] = '*'. (Though this solution may also end after one iteration based on probability)
import random, time
list=[i for i in range(10)]
print(list)
this = random.randrange(9)
while this in list:
list[this] = '*' # moved here
print(list)
this = random.randrange(9)
print(this, this in list)
time.sleep(.200)
EDIT based on comment
Since the goal is to replace integers with *'s until no integers remain in the original list, here are some modifications.
The while loop exits when the number of elements replaced is equal to the number of items in the original array. The indices that have not been replaced yet are stored in the array called available.
import random, time
lst = [i for i in range(10)]
print(lst)
num_replaced = 0
# available = list.copy()
available = list(range(len(lst)))
while num_replaced < len(lst):
this = random.choice(available)
available.remove(this) # the index just chosen is no longer available
lst[this] = '*'
num_replaced += 1

In Python: How to deal with zero at first position in condition

I found some strange behavior in python. Possibly my logic is not correct.
1 and 2 and 3 in range(5)
Expected: True
Outcome: True
2 and 1 and 99 in range(5)
Expected: False
Outcome False
2 and 1 and 0 in range(5)
Expected: True
Outcome: True
Now the tricky one:
0 and 1 and 2 in range(5)
Expected: True
Outcome: 0
I am sure there is someone who makes me find my logical error.
In each expression, only the last number is checked against the range. The previous ones are evaluated "as is". In python, the expression if i: evaluates to True if i is not 0.
The value returned from the expressions (boolean or int) depends on what the conditions are. If you leave just 1 and 2 for example, the result will be the last int. However, since you have the v in range(n) expression, which returns True or False, the result is cast into a boolean value.
Now, due to short-circuit evaluation, in the last case, only the zero gets evaluated. So the result is not cast into a boolean and 0 is returned.
Edit: After reading the comments, it becomes clear that you want to check if k number exist in range(n). For that, you cannot use the simple expressions you've shown. You need to check if every individual value exists in the range. One - inefficient - approach would be this
if all([v in range(n) for v in values]):
print("All values exist in the range")
Edit 2 (by #Pranav Hosangadi)
Side note:
Since the all() function takes generator expressions, you can avoid the list-comprehension altogether. When you do this, the generator expression will only calculate as many items as needed for the all() to short-circuit. On the other hand, the list-comprehension approach will calculate all elements in the list, and then run all() on that list. Here's a simple example:
l = [100] * 10000
l[-1] = 0
def f1(): # Using generator
return all(li == 0 for li in l)
def f2(): # Using list comp
return all([li == 0 for li in l])
Now for the generator-expression approach, all() needs to calculate only the first element to know that it will short-circuit to False. However, the list-comprehension approach calculates all elements of the list first. All but the last element of this list are False. Then, all() takes this list and short-circuits at the first element. Running some timing on these functions:
import timeit
timeit.timeit('f1()', setup='from __main__ import f1, l', number=10000)
# Out: 0.006381300001521595
timeit.timeit('f2()', setup='from __main__ import f2, l', number=10000)
# Out: 5.257489699986763
f1() is significantly faster than f2().

CodingBat - Warmup2 - ARRAY_FRONT9

I'm very new to programming and I'd like your expertise on a problem I've come across on CodingBat.
But I am not sure why my solution doesn't work. What am I missing here?
THE PROBLEM
Given an array of ints, return True if one of the first 4 elements in the array is a 9. The array length may be less than 4.
array_front9([1, 2, 9, 3, 4]) → True
array_front9([1, 2, 3, 4, 9]) → False
array_front9([1, 2, 3, 4, 5]) → False
MY SOLUTION
def array_front9(nums):
for i in range(0,len(nums)):
if len(nums)> 0 and nums[i] == 9:
return True
else:
return False
SOLUTION ONLINE
def array_front9(nums):
# First figure the end for the loop
end = len(nums)
if end > 4:
end = 4
for i in range(end): # loop over index [0, 1, 2, 3]
if nums[i] == 9:
return True
return False
Your assistance is appreciated. Thank you all.
Screenshot with the expected answers
Your solution is exiting prematurely with its return False line. As it's currently written, the code says, "Check the first number in the list. If it's a 9, end the function and return True. Otherwise, end the function and return False." That's what return statements do: end the function and (optionally) return something.
All you need to do to fix this is remove the else block and change the indentation of return False:
def array_front9(nums):
for i in range(0,len(nums)):
if nums[i] == 9:
return True
return False
(Your extra check for len(nums) > 0 is unnecessary since range(0, 0) will simply do nothing and the for loop will be skipped.)
By unindenting return False, that line will now run only after the for loop has completed, which is what you want. Note you'll still need to modify this code to check only the first 4 items in the array.
Also, outside of a learning context like CodingBat, in the real world Python does all of this work for you. You can just write 9 in nums[:4] and get your answer.
ANSWER UPDATE
Thanks to user JDAZ, I've come up with a working solution. Hoping this can shed some light towards beginners.
SOLUTION:
def array_front9(nums):
for i in range(0,len(nums)):
if nums[i] == 9 and i < 4:
return True
return False
Your code will always return False.
In your code, you create an empty list mylist and don't put anything in it. Length of such list is always 0. So, the condition in your if loop will never be true.
Solution:
def array_front9(nums):
for i in nums[0:3]:
if i == 9:
return True
return False

Python- How to fix failed testcase on a has repeat function?

I've built a function that checks for repeats of a specific number in a list named xs. V is the number to check for repeats of. It needs to return True if there are more than one occurrences of the number and if there are none, it needs to return False.
I'm failing one test case which is input xs=[1,2,1] v=1, this function needs to return True, but my code is making it False. Can you see where I went wrong?
Here is my current code:
def has_repeat(xs, v):
count=0
for num in range(len(xs)):
if num == v:
count+=1
if count>1:
return True
else:
return False
You're actually iterating over the range of the length of the list, not the items in the list.
The range function returns a list of numbers from 0 (by default) to the number you provide, in this case 3 (not inclusive). See Python documentation.
As an example if you try:
l = [1, 2, 3]
print(range(len(l)))
It will print out [0, 1, 2]
What you should do is instead of
for num in range(len(xs))
do
for num in xs:
You can try it out on PyFiddle here
As an added tasty bonus, you could change this to use the .count method on your list of items to check how many occurrences of that number are in the list, removing the need to iterate the list at all, like so:
count = xs.count(v)

Python - Create a recursion function

my question is basically this: Create a recursion function that takes a nested list as a
parameter and returns the sub-list that has minimum difference between its maximum and minimum elements.
For example: Function should return [1,2] for input [[1,199,59],[1,2],[3,8]]
I searched Google and stackoverflow, but i could not find this specific example.
What i would like to get help is with iteration. I want to, using recursion, iterate over each sub-list(can be as many as possible). I have achieved this with a for loop, but i cannot grasp the idea of iteration by using recursion method.
So far, i have this:
def sublist(mylist):
if len(mylist) == 0:
return []
elif len(mylist) == 1:
return mylist
else:
a = (mylist[0][0]) - (mylist[0][-1])
if a < sublist(mylist[1:]):
return mylist[0]
sublist([[1,199,58],[1,2],[3,8]])
This part, ( sublist(mylist[1:]) ) i know is clearly wrong. I'm trying to compare the value a, with the values from the mylist[1:]. I would appreciate much advice here.
Updated:
def differences(mylist):
diff = max(mylist) - min(mylist)
return diff
def sublist(nestedlist):
if len(nestedlist) == 1:
return nestedlist[0]
else:
if differences(nestedlist[0]) < differences(sublist(nestedlist[1:])):
return nestedlist[0]
else:
return sublist(nestedlist[1:])
print(sublist([[1,199,59],[1,2],[3,8]]))
i am assuming that you want to use recursion for the first level of the list. So, without giving you the code 100%, you have to do something like that:
1) create a method e.g diferences(list) that calculates the differences of a list and returns a list with the parameter list and the min difference i.e differences([1,2]) should return [1, [1,2]]. call it once on the first sublist i.e min = differences(mylist[0])
2) create your sublist method like this:
def sublist(initial_list):
# 1) call differences() method for the first sublist of the 'initial_list'
# 2) update 'min' with differences(initial_list[0])if differences(inilitial_list[0])[0] < min[0];
# 3) call sublist() again now removing the sublist you checked before from the arguement
# 4) (the following should be at the start of your sublist() method)
if len(initial_list) = 1:
if differences(initial_list) < min:
return initial_list
else: return min[1]
Hope that helps

Resources