Nesting Structure Comparisons - python

I'm trying to write Nesting Structure Comparison with recursion. Seems pretty basic but doesn't work: when I compare len(original) and len(other) they're different, but it doesn't return False. Why?
def same_structure_as(original,other):
if type(original) != type(other) or len(original) != len(other):
return False
for i in range(len(original)):
if type(original[i]) != type(other[i]):
return False
if type(original[i]) is list and type(other[i]) is list:
same_structure_as(original[i],other[i])
return True
print (same_structure_as([1,[1,1]], [2,[2]]))

As commented, your code did not consider the recursion result. I corrected it and used a combined loop instead of the repeated cumbersome indexing. (I still don't like the multiple return construction). Note that you don't compare simple values in the loop, so an else part for the list type comparison is still needed. Otherwise this works as expected:
def same_structure_as(original, other):
if type(original) != type(other) or len(original) != len(other):
return False
for org_val, other_val in zip(original, other):
if type(org_val) != type(other_val):
return False
if type(org_val) is list and type(other_val) is list:
if not same_structure_as(org_val, other_val):
return False
return True
print (same_structure_as([1,[1,1]], [2,[2]]))

Scratching my original answer, but this is more related to your nested array length.
len([1,[1,1]]) will have a length of 2. It will not count the inner nest items. Therefore, comparing this nested list with other list will pass on your if check.

Related

Loop through int array, return True if following int is equal to current

Given a list of ints, return True if the array contains a 3 next to a 3 somewhere.
has_33([1, 3, 3]) → True
has_33([1, 3, 1, 3]) → False
has_33([3, 1, 3]) → False
First Approch:
def has_33(nums):
for i in range(0,len(nums)):
return nums[i] == nums[i+1] ==3
Could someone explain me what's wrong with this approach, I see that this code is returning True only if all the elements in a list are true.
Second Approach:
def has_33(nums):
for i in range(0,len(nums)):
if(nums[i] == nums[i+1] ==3):
return True
The second approach satisfies my question.
What is the difference between these two approaches?
Well, the difference is rather obvious. In the first case, you inconditionnaly return the result of expression nums[i] == nums[i+1] ==3, whatever the value of this expression is. This actually means that you always return on the very first iteration, so your code could as well be written as
def has_33(nums):
if len(nums):
return nums[0] == nums[1] ==3
In the second case, you only return if the expression is true, so the iteration goes on until either you explicitely return (found a match) or the iteration naturally terminates and you've found nothing (in which case the function will implicitely return None).
Unrelated, but your code (second version) can be improved in quite a few ways. First point: Python "for" loop are of the "foreach" kind - you iterate on the sequence elements, not indices. If you don't need the indice, the proper way is
for item in iterable:
do_something_with(item)
no need for range(len(xxx)) and indexed access here.
If you do need both the item and the index, then enumerate() is your friend - it yields (index, item) tuples:
for index, item in enumerate(sequence):
print("item at {} is {}".format(index, item))
Now for your current need - geting (item, nextitem) pairs -, there's still another solution: zip(seq1, seq2) + slicing:
for item, nextitem in zip(sequence, sequence[1:]):
print("item: {} - nextitem : {}".format(item, nextitem))
and finally, if what you want is to check if at least one item in a sequence satisfies a condition, you can use any() with a predicate:
def has_33(nums):
return any((item == nextitem == 3) for item, nextitem in zip(nums, nums[1:]))
Another solution could be to turn nums into a string and look for the literal string "33" in it:
def has_33(nums):
return "33" in "".join(str(x) for x in nums)
but I'm not sure this will be more efficient (you can use timeit to find out by yourself).
In your first approach, you will return the value of
return nums[i] == nums[i+1] == 3 #Where i = 0 since it returns
first iteration.
return nums[0]==nums[1] == 3 #If nums = [0,3,3]
return false # would be your result. But it would never check the next pair of values.
In your second approach, you will return the value
return true #If the if-statement is satisfied
The return function, will end the function call when called. Therefore, if being called in a for-loop without an if-statement, it will be called for the first iteration. If there is an if-statement and the iteration passes through the if-statement, it will return and end the loop at that iteration. Basically, the return function ends the function call and returns the value given.

palindrome error with 8 or 9 letters

I have the following code, when going through the python, the options aaabaaaa, zzzazzazz gave me the false test.Here is the code, I am not too sure on how to fix it.
def checkPalindrome(inputString):
n=len(inputString)
#if string is one letter
if n==1:
return True
#if string has more than one letter
for i in range (0, math.floor(n/2)) :
if inputString[i]!=inputString[n-1-i]:
return False
else:
return True
You have a few issues. The main issue here is that your else clause has a return True inside the loop. What you'd want to do is finish iterating over the string before returning True. If you are familiar with boolean logic, this is the equivalent of short circuiting with AND.
The other issue (not really an issue, more a nitpick) is that you can just use integer division //, instead of having to import math's floor function.
So,
def isPalindrome(string):
for i in range(0, len(string) // 2):
if string[i] != string[-(i + 1)]:
return False
return True
Another way of handling this would be using all:
def isPalindrome(string):
return all(x == y for x, y in zip(string, reversed(string)))
Or, taking advantage of python's convenient slice notation for the most concise solution possible, we have:
def isPalindrome(string):
return string == string[::-1]
Try this which uses array slicing (reversing an array of chars)
def checkPalindrome(inputString):
n=len(inputString)
#if string is one letter
if n==1:
return True
#if string has more than one letter
return inputString==inputString[::-1]
Another approach could be using slicing. Strings can be accessed by index like arrays/lists and also be inverted like this.
def isPalindrom(string)
return string == string[::-1]
the [::-1] slicing returns the reversed string, the comparision with the original string is True if it's the same otherwise false.

Is this if statement well expressed?

I tried to optimize an if statement in one single line and it seems to work properly in my shell but when I test it return nothing. Do you see somethind strange?. The goal of this function is replace the if statement with a single. return statement, and thats what I did.
This is the original function:
def same_length(L1, L2):
'''(list, list) -> bool
Return True if and only if L1 and L2 contain the same number of elements.
'''
if len(L1) == len(L2):
return True
else:
return False
And this is my optimization:
return True if len(L1) == len(L2) else False # Do you see something strange here?
Is this correct?
It is correct, but you do not have to make it so complicated. It is equivalent to:
return len(L1) == len(L2)
There is no need to use an if-statement here: the result of len(L1) == len(L2) is already a boolean. If that boolean is True you return True and the same for False. Simply returning the outcome of the test is equivalent (and more efficient).

Overlapping lists function returns False when True in python

I'm a programming semi-noob and am working through Torbjoern Lager's 46 Simple Python Exercises. This is number 10: Define a function overlapping() that takes two lists and returns True if they have at least one member in common, False otherwise. You may use your is_member() function, or the in operator, but for the sake of the exercise, you should (also) write it using two nested for-loops.
def over(list1,list2):
for i in list1:
for j in list2:
return i==j
I thought I had a nice, simple solution, but it can't recognize that the lists overlap, unless the overlapping elements are the first ones.
over(["a","b","c","d"],["e","f","a","h"])
returns False
over(["a","b","c","d"],["a","f","g","h"])
returns True
For some reason, it's not searching through all of the combinations. Any help would be appreciated.
It's not searching through all the combinations because you're returning on the first iteration of the nested loop. You could do this:
def over(list1,list2):
for i in list1:
for j in list2:
if i == j:
return True
return False
This returns True as soon as any overlap is found. If no overlap is ever found, it'll get to the last line and return False.
The problem is that you return i==j on the first iteration. Your function will justs compare list1[0] and list2[0]. The solution is to add if.
Here is an example:
def over(list1,list2):
for i in list1:
for j in list2:
if i == j:
return True
return False
You should be testing with an if as suggested in the other answers as you are returning after the very first iteration but using any would be a nicer approach:
def over(list1,list2):
return any(i ==j for i in list1 for j in list2)
Which is equivalent to:
def over(list1,list2):
for i in list1:
for j in list2:
if i == j:
return True
return False
short circuiting on a match and returning True if there is any match or returning False if there are none.
Or using sets for larger input would be the fastest approach:
def over(list1, list2):
return not set(list1).isdisjoint(list2)
if not set(list1).isdisjoint(list2) is True we have at least one common element.
When you execute the "return" stament de execution stops there, like it happens in Java. It returns true because you have 'a' in the first position in both arrays.
You can try this:
result = False;
for i in list1:
for j in list2:
if i == j:
result=True;
return result
If you want it more efficient:
for i in list1:
for j in list2:
if i == j:
return True;
return False;

Python Recursively check for repeats

I have an assignment I've been stuck on for a couple days now. I have to recursively figure out if a list has repeats but I cannot use any loops or built in functions besides len(). I'm also not allowed to use the 'in' function. Returns True if list L has repeats, False otherwise. This is what I've been able to figure out:
def has_repeats(L):
if len(L) <= 1:
return False
elif L[0] == L[1]:
return True
else: return has_repeats(L[0] + L[2:])
But the problem with that is it's only comparing the first element to the rest, instead of each element to the rest. I can't figure out how to do that without a running counter or something. Any suggestions?
You almost have it. Along with checking the first element with the rest of the list, you also need to check the second the same way:
def has_repeats(L):
if len(L) <= 1:
return False
if L[0] == L[1]:
return True
if has_repeats([L[0]] + L[2:]):
return True
if has_repeats(L[1:]):
return True
return False
You can also compact this into the following representation:
def has_repeats(L):
return len(L)>1 and L[0]==L[1] or has_repeats([L[0]]+L[2:]) or has_repeats(L[1:])
Use a helper function:
def helper(ele, rest):
if not rest:
return False
return ele == rest[0] or helper(ele, l[1:])
def has_repeats(l):
if not l:
return False
return helper(l[0], l[1:]) or has_repeats(l[1:])

Categories