I want to check if a string is abecedarian - python

I want to check if a string is abecedarian (in alphabetical order).
def is_abecdarian(s):
index = 0
while index < len(s)-1:
if s[index + 1] > s[index]:
return True
index += 1
return False
print(is_abecdarian('babcd'))
If I switch my False and True around and switch the greater than to less than it works. What I do not understand is 'a' is not greater than 'b', yet when I run the above code it returns True.

The return True statement exits the function, so right now it runs until there are two letters that are in a correct order and then exits.

The way your loop is written, it will return True if any pair of characters is ordered. It only returns False if all pairs fail. The first pair ba fails the if check, but the second pair ab passes and the function returns True.
This isn't right, because if you think about it, you can't know that a string is abecedarian until you've examined all of the characters. You should only be returning True when you're done with the loop. Conversely, as soon as you find a pair of characters that's out of order, it is safe to return False.
That's why flipping the booleans and the < operation works.

You need to return False only if next character is < than current.
If there were no such an occurrence during all the loop then return True.
Here is correct function:
def is_abecdarian(s):
index = 0
while index < len(s)-1:
if s[index + 1] < s[index]:
return False
index += 1
return True
print(is_abecdarian('babcd'))
print(is_abecdarian('abcz'))

Why it's not working has already been explained, so, only to add to what has been explained:
def is_abecdarian(s):
return all(s[i] < s[i+1] for i in range(len(s)-1))
print(is_abecdarian('babcd'))
print(is_abecdarian('ejmpq'))
Where
all(argument) returns True only if all boolean in the argument are True;
s[i] < s[i+1] is a comparison between the i-th element of s to the next one.
s[i] < s[i+1] for i in range(len(s)-1) makes the comparison for all pairs of consecutive elements of s.

Related

Recursive function without loops or variables

I am learning coding by myself. I am taking learning recursive function from online resources, and as an exercise, I came across this one exercise where I am tasked to create a recursive function to see if numbers in the list are in order. The exercise says not to use loops and not to use variables, so I can learn recursive function better for the future.
def is_increasing(lst):
if not (lst[0]) < lst[1]:
return False
else:
return is_increasing(lst[1:])
when I input is_increasing([1,2,12,24]) in the console.
The errors says:
IndexError:list index out of range
What am I doing wrong that is showing this error?
You need a stop condition (or base case) for every recursive function, otherwise it won't stop: in your case, the stop condition is if the list contains one elements or less, it's always sorted:
def is_increasing(lst):
if len(lst) <= 1: return True # Base case
if not (lst[0]) < lst[1]:
return False
else:
return is_increasing(lst[1:])
You didn't take the recursion far enough. You are assuming that a list will have at least two elements, but that's not true for empty and singleton lists. (Plus, at no point did you ever attempt to return True.)
The true base case is a list with fewer than 2 items, which is trivially sorted. Otherwise, you can check whether the first 2 elements are in order, then recursively check the rest of the list.
def is_increasing(lst):
if len(lst) < 2:
return True
else:
return lst[0] < lst[1] and is_increasing(lst[1:])
When implementing a recursive function, there is always one special case which has to be handled differently: how does the recursion stop?
The implementation show here correctly reduces the problem of solving an input of size N to a problem of size N-1:
def is_increasing(lst):
if not (lst[0]) < lst[1]:
return False
else:
return is_increasing(lst[1:])
What is missing is handling the situation where the size is 0 or 1. Therefore:
def is_increasing(lst):
if len(lst) < 2:
return True # That is always sorted
# BTW this is correct only if it is supposed to be descending,
# oherwise it should be lst[0] > lst[1]
if not lst[0] < lst[1]:
return False
else:
return is_increasing(lst[1:])
Your function never returns True. That's not directly the cause of the error, but that's related to it.
For a recursive function that returns a bool, you need two base cases, not just one: one that returns True and one that returns False.
So when should it return True? What is the relevant base case? It is when the list is too small to fail: a list that is of length 1 or 0 is always increasing, technically. And in that case, there is no lst[1], which causes the IndexError. So this is the solution:
def is_increasing(lst):
if len(lst) < 2:
return True
elif lst[0] >= lst[1]:
return False
else:
return is_increasing(lst[1:])
(You also had a mistake where you compare not lst[0] with lst[1], I fixed that for you ;)

Can some one help me fix this list sorting code?

When I run this code it doesn't print anything?
I've tried to invert the greater than less than but it's not helping.
def is_sorted(numbers):
''' Return whether or not the list of numbers is sorted '''
for i in numbers:
if numbers[i] < numbers[i + 1]:
print('True')
else:
print('False')
for i in numbers in this case i is each number itself, not the index of each number. That would be for i in range(len(numbers)) though then [i + 1] would go out of bounds on the last iteration.
As an alternative, I would simplify that function to compare each element to the next element by zipping a slice of the list against another slice offset by one. By writing this as a generator expression within all this will short-circuit upon hitting the first False
def is_sorted(numbers):
return all(i <= j for i, j in zip(numbers, numbers[1::]))
For example
>>> is_sorted([1,3,5,7])
True
>>> is_sorted([1,6,2,7,3])
False
Something like this should work:
def is_sorted(numbers):
isSorted = True
for i in numbers:
try:
if i > numbers[numbers.index(i)+1]:
isSorted = False
except:
pass
return isSorted
print(is_sorted([1,2],3,9,3))
You should add the variable, isSorted, because if i > next number, it would print False, but if i < next number, it would print True. You need the try and except, because if there was an error, if it was on the last number, it would be ignored. You also need to change i > numbers[i+1] to i > numbers[numbers.index(i)+1], because to get the next number, we need to add 1 to the index, not the value.

How to decrease execution time limit for a list-traverser

I'm working on a Python problem on Code Signal and I'm trying to see if a given list is a strictly increasing sequence when only one element is removed from it. So I've built code that, in a for-loop, removes element i from the list and checks if it is an increasing sequence and then replaces that element at that exact index and starts over. This is the code:
def almostIncreasingSequence(sequence):
for i in range(len(sequence)):
element = sequence[i]
del sequence[i]
if all(i < j for i, j in zip(sequence, sequence[1:])):
return True
sequence.insert(i, element)
return False
The code works well but it causes an error with the execution time. Is there any way I can improve this existing code so it runs faster?
It would be quicker to run through the list, comparing each value to the previous to ensure it's strictly increasing. Allow this to not be true for one number in the list and skip this number. Unfortunately it's not quite that simple, as we can see below:
Won't work (eg. [1,4,2,3])
def almostIncreasingSequence(sequence):
lastValue = sequence[0]
removed_value = False
for i in range(1,len(sequence)):
if sequence[i] <= lastValue:
if removed_value:
return False
else:
removed_value = True
else:
lastValue = sequence[i]
return True
Instead, we need to cover the two possibilities if we encounter a non-increase: remove the current number (eg. [1,2,1,3]) or remove the previous (eg. [1,2,8,4]). We also have some edge cases for removing the first or last number in the list.
Final (not so pretty) solution
def almostIncreasingSequence(sequence):
lastValue = sequence[0]
skipped_value = False
for i in range(1,len(sequence)):
if sequence[i] <= lastValue:
if i+1 == len(sequence):
return not skipped_value # last number is not decreasing, skip if we can
if skipped_value:
# if we've already skipped a number - won't work
return False
elif sequence[i+1] > sequence[i-1]:
# skipping the current number will fix it
skipped_value = True
lastValue = sequence[i-1]
else:
# try and skip the previous number
skipped_value = True
if i == 1 or sequence[i] > sequence[i-2]:
# can skip the previous number and it'll work
lastValue = sequence[i]
else:
# we have no chance
return False
else:
lastValue = sequence[i]
return True

How can I check if a list index exists?

Seems as though
if not mylist[1]:
return False
Doesn't work.
You just have to check if the index you want is in the range of 0 and the length of the list, like this
if 0 <= index < len(list):
it is actually internally evaluated as
if (0 <= index) and (index < len(list)):
So, that condition checks if the index is within the range [0, length of list).
Note: Python supports negative indexing. Quoting Python documentation,
If i or j is negative, the index is relative to the end of the string: len(s) + i or len(s) + j is substituted. But note that -0 is still 0.
It means that whenever you use negative indexing, the value will be added to the length of the list and the result will be used. So, list[-1] would be giving you the element list[-1 + len(list)].
So, if you want to allow negative indexes, then you can simply check if the index doesn't exceed the length of the list, like this
if index < len(list):
Another way to do this is, excepting IndexError, like this
a = []
try:
a[0]
except IndexError:
return False
return True
When you are trying to access an element at an invalid index, an IndexError is raised. So, this method works.
Note: The method you mentioned in the question has a problem.
if not mylist[1]:
Lets say 1 is a valid index for mylist, and if it returns a Falsy value. Then not will negate it so the if condition would be evaluated to be Truthy. So, it will return False, even though an element actually present in the list.
In the EAFP style of Python:
try:
mylist[1]
except IndexError:
print "Index doesn't exist!"
In the case of integer-indexed lists, I'd simply do
if 1 < len(mylist):
...
For dicts, you can of course do
if key in mydict:
...
Alternative (but somewhat slower) way of doing it:
if index not in range(len(myList)):
return False
It gets a bit more verbose when accounting for negative indices:
if index not in range(-len(myList), len(myList)):
return False
assert len(mylist) >= abs(index) + int(index >= 0), "Index out of range"
or
assert len(mylist) > abs(index) - int(index < 0), "Index out of range"
Or you can do:
if index in dict(enumerate(mylist)):
return True
Although is will probably be even less efficient than range(len(mylist)). Maybe someone should propose a keys() method for lists that returns the range of the keys in PEP.
The following approach returns the True result for index == 0 and for negative index values (if such an index is valid for the list, for example listIn[-2] for [0, 1, 2]):
def isInListRange(listIn, index):
""" Description: Function to detect if list index out of range
Import: from shared.isInListRange import isInListRange
Test: python -m shared.isInListRange
"""
try:
return True if (index == 0 and len(listIn) > 0) or listIn[index] else False
except:
return False

Python code explanation need

def array_front9(nums):
end = len(nums)
if end > 4:
end = 4
for i in range(end):
if nums[i]==9:
return True
return False
I need to understand the above python code and why two return statement in 'for loop'. This is seriously confusing me.
This could be rewritten much simpler (that is, "more pythonic") as this:
def array_front9(nums):
return 9 in nums[:4]
The first half of the code is setting the loop limit to the first 4 elements, or less if the array nums is shorter. nums[:4] does essentially the same thing by creating a copy that only contains up to the first 4 elements.
The loop is checking to see if the element 9 is found in the loop. If found, it returns immediately with True. If it's never found, the loop will end and False is returned instead. This is a longhand form of the in operator, a built-in part of the language.
Let me explain:
def array_front9(nums): # Define the function "array_front9"
end = len(nums) # Get the length of "nums" and put it in the variable "end"
if end > 4: # If "end" is greater than 4...
end = 4 # ...reset "end" to 4
for i in range(end): # This iterates through each number contained in the range of "end", placing it in the variable "i"
if nums[i]==9: # If the "i" index of "nums" is 9...
return True # ...return True because we found what we were looking for
return False # If we have got here, return False because we didn't find what we were looking for
There are two return-statements in case the loop falls through (finishes) without returning True.
The second return isn't in the for loop. It provides a return value of False if the loop "falls through", when none of nums[i] equal 9 in that range.
At least, that's how you've indented it.
You could rewrite this to be more clear using list slicing:
def array_front9(nums):
sublist = nums[:4]
if 9 in sublist:
return True
else:
return False

Categories