Make a recursive function exit if a condition is met? - python

I'm trying to solve this problem in Python: "Write a function to return True when its argument is an array that has the same nesting structures and same corresponding length of nested arrays as the first array."
So I want to compare 2 lists, check if their elements are lists, and if so then check if the elements of these lists are lists, and so on, until I find the point where "original" doesn't match "other.
So far I've got:
def same_structure_as(original,other):
print(type(original))
print(type(other))
# if either, but not both of original and other are a list, return False
if (type(original) is list) != (type(other) is list):
print("not match")
return False
print()
if (type(original) is list):
for i in range(len(original)):
if (type(original[i]) is list):
same_structure_as(original[i],other[i])
return True
print(same_structure_as([ 1, [ 1, 1 ] ], [ 2, 5 ] ))
However, the problem is that once I get to the "return False", it only returns False for this iteration. So the iterations above this will still return True, giving the below output when run:
<class 'list'>
<class 'list'>
<class 'list'>
<class 'int'>
not match
True
whereas I want the entire program to exit and return False immediately after "not match" gets printed...

Adding a check on the line of recursive calling function may help you to resolve your problem. By doing that when ever there is a mismatch it will return back False through all the steps of recursion.
def same_structure_as(original,other):
print(type(original))
print(type(other))
# if either, but not both of original and other are a list, return False
if (type(original) is list) != (type(other) is list):
print("not match")
return False
print()
if (type(original) is list):
for i in range(len(original)):
if (type(original[i]) is list):
if(same_structure_as(original[i],other[i])==False):
return False
return True
print(same_structure_as([ 1, [ 1, 1 ] ], [ 2, 5 ] ))

The issue is that you are calling same_structure_as for the elements of the list, but you are not using the return value.
The recursive part is called for each element of the list. You need to combine them together and return the combined result. The idea is that for two lists to return True, you need the following condition:
same_as(a[0], b[0]) && same_as(a[1], b[1]) && ... && same_as(a[n], b[n])
The logic of the function should be something like this:
def same_as(a ,b):
# If the two elements have different types, then they're definitely not matching
if type(a) != type(b): return False
# Now both elements are the same type.
# If they are not lists, it means there is no nested elements. So they have the same structure
If type(a) is not list: return True
# At this point, both elements are lists. Check first if their sizes are the same
if len(a) != len(b): return False
# Now the elements are lists of the same size. Compare each sub-element inside a loop
# For the two lists to be the same structure, *ALL* their elements must be the same structure.
# We will use the AND operator
equal = False
For i = 0:len(a):
equal = equal && same_as(a[i], b[i])
# Now the boolean 'equal' will be True ONLY if ALL the sub-elements returned True
return equal

Related

Check if a python list contains numeric data

I am checking whether a list in python contains only numeric data. For simple ints and floats I can use the following code:
if all(isinstance(x, (int, float)) for x in lstA):
If there any easy way to check whether another list is embedded in the first list also containing numeric data?
You can do a recursive check for all lists within the list, like so
def is_all_numeric(lst):
for elem in lst:
if isinstance(elem, list):
if not is_all_numeric(elem):
return False
elif not isinstance(elem, (int, float)):
return False
return True
print(is_all_numeric([1,2,3]))
>>> True
print(is_all_numeric([1,2,'a']))
>>> False
print(is_all_numeric([1,2,[1,2,3]]))
>>> True
print(is_all_numeric([1,2,[1,2,'a']]))
>>> False
I don't know if there is another way to do this, but you could just do a for loop for each item, and if any of those items is not a number, just set on bool to false:
numbers = [1,2,3,4,5,6,7,8]
allListIsNumber = True
for i in numbers:
if i.isnumeric() == False:
allListIsNumber = False
You can either use isnumeric() or isinstance()

Python Check if at least 1 item in a 2d list is None

Consider some 2d lists:
a = [[1,2,3,4],
[5,6,7,None]]
b = [[1,2,3,4],
[5,6,7,8]]
How to check if there is at least one None in a 2d list?
Outputs:
deal with a should output a bool value False, and b should output True.
I have no ideas when the list be a 2d list.
You can use two loops, one inside the other.
def at_least_one_none(array):
for row in array:
for item in row:
if item == None:
return True
return False
This can be simplified by using None in row rather than the inner loop.
def at_least_one_none(array):
for row in array:
if None in row:
return True
return False
Either could be written using any() with a generator expression:
def at_least_one_none(array):
return any(None in row for row in array)
And at that point you barely need the function.
This function returns True if the a 2nd list contains at least one None, otherwise it returns False
a = [[1,2,3,4],
[5,6,7,None]]
b = [[1,2,3,4],
[5,6,7,8]]
def fun(a_list):
for l in a_list:
if None in l:
return True
return False
print(fun(a))
print(fun(b))
Output:
True
False
Here, you first merging all the lists into one, and then checking that value is None or not individually, and returning [True, True,..False]. Lastly check if there is any True in it.
Code:
True in [i==None for i in sum(<YourList>,[])] #returning True with None
Or filter out None from list and then check if it still the same size
list(filter(None, sum(<Yourlist>,[])))!=len(<Yourlist>) #returning False with None

Python, check whether array elements are all same or not

I have an array, say [4 4 4 4 4], here the length is 5. In real case it could be 300. How to check whether all the elements are same, say in this case all are 4. If all elements have same value, the function return true, otherwise false. The element could be only interger and value could be either one of them: 0,1,2,3,4.
I could use a loop in Python as follows. But I am looking for a concise way or simple way to do that, say one line.
x= [4,4,4,4]
temp = x[0]
for ele in x:
if(temp != ele):
false
true
You can put elements into set() and then check if length of the set is equal to 1:
if len(set(x)) == 1:
print('All elements are same')
else:
print('Not same')
it might be more efficient not to iterate over the full list (as does the set constructor) but to stop at the first element that does not equal x0. all does that for you:
x = [4,4,4,4]
x0 = x[0]
print(all(item == x0 for item in x))
# True
this is basically the same version you had; only the loop will be much more efficient this way.
also note that true and false are not valid python identifiers. in python it is True and False.

if...else.. compare character list and string by python

I am writing an for loop comparing the character in list and string, then return False if the character is not same. But I keep receiving True. Can someone explain what is wrong with my code?
def compare (sofar, orig):
if len(sofar) == len(orig):
for i in range(len(orig)+1):
if orig[i] == sofar[i]:
return True
else:
return False
return False
here is the result i got:
In [29]: compare (['a','v','c','c','s'], 'abccs')
Out[29]: True
But it suppose to be False
You could just join the characters back into a string
def compare (sofar, orig):
return ''.join(sofar) == orig
Otherwise, if you wanted a loop, you need to compare all characters before you return True. You can return False when the first does not match
Here's another one liner using all() over a loop of zipped characters.
You will need the length check before this statement
return all(x == y for x, y in zip(sofar, orig))
Or going back to the original code, invert your logic
def compare (sofar, orig):
if len(sofar) != len(orig):
return False
for i in range(len(orig)):
if orig[i] != sofar[i]:
return False
return True
If your function reaches a return statement, that's it. Your function has returned a value and it will not continue running.
You can use a generator to implement your logic. The below solution allows you to iterate lazily; retrieve a Boolean list with a letterwise comparison via list; or check if all letters align via all.
def compare(sofar, orig):
if len(sofar) == len(orig):
for i in range(len(orig)):
if orig[i] == sofar[i]:
yield True
else:
yield False
res = list(compare(['a','v','c','c','s'], 'abccs'))
# [True, False, True, True, True]
res = all(compare(['a','v','c','c','s'], 'abccs'))
# False
Another way of writing your logic without explicit iteration:
lst = ['a','v','c','c','s']
mystr = 'abccs'
res = ''.join(lst) == mystr
The statement return is exit from your function.
So your prog only compares the first element.
if orig[i] != sofar[i]:
return False
would do it

Looping with Consecutive Elements

I'm a beginner in Python programming and I'm having some trouble with some list stuff.
So I want to write a function that returns a boolean value. The function will inform the user whether there is a duplicate consecutive pair of elements in the list he or she entered. By the way, I want to do this using only len(), range(), loops, if-statements, variables and arithmetic (no built in functions).
For example:
contains_consecutive_duplicates([1,3,3]) should return True
contains_consecutive_duplicates([3,2,3]) should return False
My code:
def contains_consecutive_duplicates(xs):
for i in xs:
if xs[i] == xs[i-1] or xs[i] == xs[i+1]:
return True
else:
return False
My logic to this was as follows: Each time the for loop would run, i would refer to some element in the list. If that element was such that the element before it or after it was equal to it, then the for loop would return true. If not, keep searching. If there are none, it would return false.
Now, I actually understand where the error is (I think). The problem is that I don't know how to solve it. I think the for loop is running into a problem at the beginning (when i is reffering to the 0th element). There is no element before the 0th element, hence the error:
"index out of range"
P.S: Is that how you return a boolean value?
Is there a better way to do this?
I would appreciate any help given! Thanks in advance!
#roeland pointed out the problem with assuming iterating the list directly would get you indices (if you want index and value, use enumerate). But in this case you don't actually need the index.
For the simple case (where it's known to be a container you can slice), you can just iterate with zip over offset slices:
def contains_consecutive_duplicates(xs):
return any(x == y for x, y in zip(xs, xs[1:]))
More general solutions can be made with itertools.groupby to handle the case where you can't slice, but this is simple and involves no imports.
Use of any in this case is just a short hand to slurp the generator expression and return True if any values are truthy. The long equivalent form is:
def contains_consecutive_duplicates(xs):
for x1, x2 in zip(xs, xs[1:]):
if x1 == x2:
return True
return False
Since your teacher apparently thinks built-ins are bad, but len and range aren't built-ins (they are), you can do this the dumb way:
def contains_consecutive_duplicates(xs):
for i in range(len(xs) - 1):
if xs[i] == xs[i+1]:
return True
return False
Which does the same work as ziping, just less efficiently (generating index integers and explicitly indexing is surprisingly expensive in Python relative to native iteration).
This should do:
>>> def contains_consecutive_duplicates(xs):
... for i, v in enumerate(xs[:-1]):
... if v == xs[i+1]:
... return True
... else:
... pass
... return False
>>> l1 = [1,3,3]
>>> l2 = [1,3,2]
>>> l3 = []
>>> l4 = [2]
>>> contains_consecutive_duplicates(l1)
True
>>> contains_consecutive_duplicates(l2)
False
>>> contains_consecutive_duplicates(l3)
False
>>> contains_consecutive_duplicates(l4)
False
>>>
By using only range, for and if statements, this can be done with:
def contains_consequtive_duplicates(xs):
for i in range(len(xs)-1):
if xs[i] == xs[i+1]: return True
return False
You access lists with their index and not by their value (which you are by using for i in list). Additionally, if you perform the check xs[i] == xs[i-1] this will not yield the result you want since x[-1] will check the end of the list so [3, 2, 3] will return True.
As a small demonstration:
if contains_consequtive_duplicates([1,3,3]): print "consecutive"
# Prints Consequtive
if contains_consequtive_duplicates([3, 2, 3]): print "consecutive"
# Prints nothing
Try this:
def contains_consecutive_duplicates(xs):
for i in xs:
if xs.indexOf(i)==len(xs):
break
if xs[i] == xs[i-1] or xs[i] == xs[i+1]:
return True
else:
return False
What you are doing is trying to do is evaluate xs[i+1], but that does not exist.
This should do the trick:
def contains_consecutive_duplicates(xs):
for i in range(1, len(xs) - 1):
if xs[i] == xs[i-1] or xs[i] == xs[i+1]:
return True
return False
It iterates through all values bar the first and last (created by the range function), returning (which ends the loop) if it finds a duplicate.
If it reaches the end and hasn't found a duplicate one must not exist, so it returns False.

Categories

Resources