Is there any way to write with less "nesting"? - python

The problem is a homework problem. I think I solved it but I'm wondering is it the best solution? The problem is to take a list and run through it to see if it contains a specific subsequence: 007. Here subsequence is used in the mathematical sense (so 0110227 does contain 007 and returns True).
def spy_game(nums):
if 0 in nums:
a_1=nums.index(0)
nums_1=nums[a_1+1:]
if 0 in nums_1:
a_2=nums_1.index(0)
if 7 in nums[a_2+1:]:
return True
else:
return False
else:
return False
else:
return False

Start by negating your test condition so you can simply return early. The last test doesn't require an if statement at all, since by that point the result of the condition is the return value of the function.
def spy_game(nums):
if 0 not in nums:
return False
a_1 = nums.index(0)
nums_1 = nums[a_1+1:]
if 0 not in nums_1:
return False
a_2 = nums_1.index(0)
return 7 in nums[a_2+1:]

If you negate your inclusion conditions you can return early rather than introducing further nesting.
def spy_game(nums):
if 0 not in nums:
return False
a_1 = nums.index(0)
num_1 = nums[a_1+1:]
if 0 not in nums_1:
return False
a_2 = nums_1.index(0)
return 7 in nums_1[a_2+1:]
You can also write this without creating any copies of the list by using the start parameter to the index method, which will tell the index method the index at which to start searching for the element. This code looks different but achieves the same thing and may be easier to maintain for you.
def spy_game(nums):
try:
start = 0
for n in [0, 0, 7]:
start = nums.index(n, start)
return True
except ValueError:
return False

It's better to design your functions so that they don't depend on the specific input. For example:
def contains_anywhere(s, search):
i = 0
for c in search:
try:
i = s.index(c, i) + 1
except ValueError:
return False
return True
ok = contains_anywhere([0,1,1,0,2,2,7,2], [0,0,7])

Just another option, I personally don't see an issue with the nesting here.
This is shortening it up:
def spy_game(nums):
if 0 in nums:
nums_1 = nums[nums.index(0)+1:]
if 0 in nums_1:
if 7 in nums[nums_1.index(0)+1:]:
return True
return False
output:
>>> spy_game([1,0,7])
False
>>> spy_game([0,1,7])
False
>>> spy_game([1,0,1])
False
>>> spy_game([0,0,7])
True

Your code hides what you are trying to do. Try this alternative. even though it still has 3 levels of nesting, it is clearer about what it is doing:
def spy_game(nums):
search_for = [x for x in "007"]
for n in nums:
if not search_for: # found everything we were looking for
return True
if n == search_for[0]: # found the next item
search_for.pop(0)
return False

Related

how to check if a list is a consecutive number in python?

How to write python code that let the computer know if the list is a right sequence and the position doesn't matter, it will return true, otherwise it return false.
below are some of my example, I really don't know how to start
b=[1,2,3,4,5] #return true
b=[1,2,2,1,3] # return false
b=[2,3,1,5,4] #return true
b=[2,4,6,4,3] # return false
sort function is O(nlogn), we can use for loop which is O(n):
def check_seq(in_list):
now_ele = set()
min_ele = max_ele = in_list[0]
for i in in_list:
if i in now_ele:
return False
min_ele = min(i, min_ele)
max_ele = max(i, max_ele)
now_ele.add(i)
if max_ele-min_ele+1 == len(in_list):
return True
return False
Create a set and one to compare with -- based on minimum and maximum:
isRightSequence = set(range(min(b), max(b)+1)) == set(b)
This question is quite simple and can be solved a few ways.
The conditional approach - if there is a number that is bigger than the length of the list, it automatically cannot be a sequence because there can only be numbers from 1-n where n is the size of the list. Also, you have to check if there are any duplicates in the list as this cannot be possible either. If none of these conditions occur, it should return true
Using dictionary - go through the entire list and add it as a key to a dictionary. Afterwards, simply loop through numbers 1-n where n is the length of the list and check if they are keys in the dictionary, if one of them isn't, return false. If all of them are, return true.
Both of these are quite simply approaches and you should be able to implement them yourselves. However, this is one implementation for both.
1.
def solve(list1):
seen = {}
for i in list1:
if i > len(list1):
return False
if i in seen:
return False
seen[i] = True
return False
def solve(list1):
seen = {}
for i in list1:
seen[i] = True
for i in range (1, len(list1)+1):
if i not in seen:
return False
return True
This solution needs O(n) runtime and O(n) space
def is_consecutive(l: list[int]):
if not l:
return False
low = min(l)
high = max(l)
# Bounds Check
if high - low != len(l) - 1:
return False
# Test all indices exist
test_vec = [False] * len(l) # O(n)
for i in range(len(l)):
test_vec[l[i] - low] = True
return all(test_vec)
assert is_consecutive(range(10))
assert is_consecutive([-1, 1,0])
assert not is_consecutive([1,1])
assert not is_consecutive([1,2,4,6,5])

007 spy question in python. what am i missing?

I am trying to solve a problem called spy game in which you need to return true if there is 0,0,7 in order but they don't need to be consecutive, and otherwise, it should return false.
For example:
spy_game([1,2,4,0,0,7,5]) should return True
spy_game([1,0,2,4,0,5,7]) should return True
spy_game([1,7,2,0,4,5,0]) should return False
here is my code:
def spy_game(nums):
for x in range(4):
if nums[x]==0:
for y in range(x+1,len(nums)):
if nums[y]==0:
for z in range(y+1,len(nums)):
if nums[z]==7:
return True
What I am lacking is though the return is false if the given condition is false. I couldn't find an appropriate place to put a return false because otherwise, it affects the solution. My solution seems to be right for returning true. How can I solve this? Thanks in advance.
you can delete all numbers that you don't want and search '007', like:
myList = [1,2,4,0,0,7,5]
myList = [str(x) for x in l if x==0 or x==7 ] #just 0 or 7
strOfMyList = ''.join(myList)
print(strOfMyList.find('007')>=0)
#find return -1 if doesn't find else the first occurrence
In a function it looks like:
def spy_game(nums):
nums = [str(x) for x in nums if x==0 or x==7 ]
strOfMyList = ''.join(nums)
return strOfMyList.find('007')>=0

Python, check if sets check out

I have made the function, but I need to make a guess so it will run through the function and check if it fits, if not, start over again with new numbers.
If I find a set that works, the loop should break, the problem is that I am new to python and math programming.
def checkStuff(X):
ok = True
#i.
if(min(X) <= 0):
ok = False
#ii.A
A = set()
for x in X:
A.add(x % 2)
#ii.B
B = set()
for y in X:
B.add(y**2)
#ii.C
C = set()
for z in X & B:
C.add(z**0.5)
#ii.D
D = set()
for w in C:
D.add(w**2)
#iii.
if(len(X)<=0):
ok = False
#iv.
if(len(X) not in X):
ok = False
#v.
if len(A) in X:
ok = False
#vi.
if sum(X) not in B:
ok = False
#vii.
if sum(X&B) in B:
ok = False
#viii.
if sum(C.union(D)) not in X:
ok = False
return ok
without giving you the exact code, try looking at the while loop and the random function
Your function can be simplified and optimized, returning as soon as possible, avoiding further computations... for compactness I used set comprehensions instead of your loops
def checkStuff(X):
if(min(X) <= 0): return False
if(len(X)<=0): return False
if(len(X) not in X): return False
A = {x % 2 for x in X}
if len(A) in X: return False
B = {x**2 for x in X}
if sum(X) not in B: return False
if sum(X&B) in B: return False
C = {xb**0.5 for xb in X&B}
D = {c**2 for c in C}
if sum(C.union(D)) not in X: return False
return True
Assuming that you have a function that returns a list of trial sets or, possibly better, yields a new trial set for each loop, and that you want to use ONLY the first X that matches your conditions, then you can write your stuff like this
for X in generate_trial_sets():
if checkStuff(X):
do_stuff(X)
break
else:
print("No X was generated matching the criteria")
...
Note that the else clause is aligned correctly, because Python has a for ... else .. control flow construct.
Blind Attempt at a generate_trial_sets Function
Given that each X is a set of numbers (integers? reals? complex numbers? who knows? you, but you didn't care to tell...) and that we don't know how many numbers you want in the set, and also that you want to stop the iteration somehow, I'd write
def generate_trial_sets(nmin=1, nmax=5,
xmin=0.0, xmax=10.0, iterations=10):
from random import randint
for _ in range(iterations):
n = randint(nmin,nmax+1)
x = {n}
for i in range(1,n):
x.add((xmax-xmin)*random()+xmin)
yield x
When you call it like
for X in generate_trial_sets():
without modifying the default args, you get back 10 sets of length comprised between 1 and 5, with real values comprised between 0 and 10 (one of the values is equal to the length, so one of your tests is automatically fulfilled).
To use different parameters, specify them at the invocation:
for X in generate_trial_sets(nmin=6,nmax=6,xmax=100.0,iterations=200):
This is not a solution of your problem but if you understand the logic you'll get started in the right direction or, at least, I hope so...

How can I return false if more than one number while ignoring "0"'s?

This is a function in a greater a program that solves a sudoku puzzle. At this point, I would like the function to return false if there is more then 1 occurrence of a number unless the number is zero. What do am I missing to achieve this?
L is a list of numbers
l =[1,0,0,2,3,0,0,8,0]
def alldifferent1D(l):
for i in range(len(l)):
if l.count(l[i])>1 and l[i] != 0: #does this do it?
return False
return True
Assuming the list is length 9, you can ignore the inefficiency of using count here (Using a helper datastructure - Counter etc probably takes longer than running .count() a few times). You can write the expression to say they are all different more naturally as:
def alldifferent1D(L):
return all(L.count(x) <= 1 for x in L if x != 0)
This also saves calling count() for all the 0's
>>> from collections import counter
>>> def all_different(xs):
... return len(set(Counter(filter(None, xs)).values()) - set([1])) == 0
Tests:
>>> all_different([])
True
>>> all_different([0,0,0])
True
>>> all_different([0,0,1,2,3])
True
>>> all_different([1])
True
>>> all_different([1,2])
True
>>> all_different([0,2,0,1,2,3])
False
>>> all_different([2,2])
False
>>> all_different([1,2,3,2,2,3])
False
So we can break this down into two problems:
Getting rid of the zeros, since we don't care about them.
Checking if there are any duplicate numbers.
Striping the zeros is easy enough:
filter(lambda a: a != 0, x)
And we can check for differences in a set (which has only one of each element) and a list
if len(x) == len(set(x)):
return True
return False
Making these into functions we have:
def remove_zeros(x):
return filter(lambda a: a != 0, x)
def duplicates(x):
if len(x) == len(set(x)):
return True
return False
def alldifferent1D(x):
return duplicates(remove_zeros(x))
One way to avoid searching for every entry in every position is to:
flags = (len(l)+1)*[False];
for cell in l:
if cell>0:
if flags[cell]:
return False
flags[cell] = True
return True
The flags list has a True at index k if the value k has been seen before in the list.
I'm sure you could speed this up with list comprehension and an all() or any() test, but this worked well enough for me.
PS: The first intro didn't survive my edit, but this is from a Sudoku solver I wrote years ago. (Python 2.4 or 2.5 iirc)

Sorted values in increasing order and adjacent values

In python, I am trying to check if a given list of values is currently sorted in increasing order and if there are adjacent duplicates in the list. If there are, the code should return True. I am not sure why this code does not work. Any ideas? Thanks in advance!!
def main():
values = [1, 4, 9, 16, 25]
print("Return true if list is currently sorted in increasing order: ", increasingorder(values))
print("Return true if list contains two adjacent duplicate elements: ", twoadjacentduplicates(values))
def increasingorder(values):
hlist = values
a = hlist.sort()
if a == hlist:
return True
else:
return False
def twoadjacentduplicates(values):
ilist = values
true = 0
for i in range(1, len(ilist)-1):
if ilist[i] == ilist[i - 1] or ilist[i] == ilist[i + 1] :
true = true + 1
if true == 0:
return False
if true > 0:
return True
main()
Your increasingorder function will almost certainly not work, because Python uses references, and the sort function modifies a list in-place and returns None. That means that after your call a = hlist.sort(), both hlist will be sorted and a will be None. so they will not compare equal.
You probably meant to do the following, which will return a sorted list instead.
a = sorted(hlist)
This function works:
def increasingorder(values):
hlist = values
a = sorted(hlist)
if a == hlist:
return True
else:
return False
You can of course simplify this down to a single line.
def increasingorder(values):
return sorted(values) == values
Your second function looks logically correct, but can be simplified down to the following.
def twoadjacentduplicates(values):
for i in range(0, len(values)-1):
if values[i] == values[i + 1] :
return True
return False
Try creating a True False function for each value check operation you want done taking the list as a parameter. then call each function like "if 1 and 2 print 3" format. That may make thinking through the flow a little easier.
Is this kind of what you were wanting?
def isincreasing(values):
if values==sorted(values):
return True
return False
def has2adjdup(values):
for x in range(len(values)-1):
if values[x]==values[x+1]:
return True
return False
if isincreasing(values) and has2adjdup(values):
print "True"

Categories