How to check consecutive number in list? - python

This is my homework.
The problem is to find a way to check whether the items in a list are consecutive or not.
The following is code I've written:
def consecutive(var):
for x in range(2, len(var)):
forward = var[x] - var[x-1]
backward = var[x-1] - var[x-2]
if forward == backward:
return True
else:
return False
var = []
print 'Enter your number:'
while True:
num = raw_input()
if num == '':
break
var += [int(num)]
print consecutive(var)
If I input numbers like 1, 2, 3, 4, 5 then I will get True
If I input numbers like 2, 6, 3, 9, 7, 1, 4 then I'll get False
Here, I succeeded returning True or False values respectively.
But there're two questions that make me upset because if I use my code to solve the questions, I don't get the value that I want (it gives me an error)
First question: Is an empty list considered a consecutive list or not?
Second: Is a list that involves a single value considered a consecutive list or not?
Would you like to help me?

By mathematical convention as discussed in the comments, it would be standard to consider both an empty list and a list with a single element as consecutive, should it not be specified in your homework more precisely.
By the logic of vacuous truth, were a list to not have consecutive order it would require enough elements (2) to break this condition of ordering. To handle this in your existing code, you could simply perform a check before your main check to ensure the base cases of an empty list and a list with one element return True.

Related

Built in (remove) function not working with function variable

Have a good day everyone, pardon my lack of understanding, but I can't seem to figure out why python built in function does not work when being called with another function variable and it just doesn't do what I want at all. Here is the code
def ignoreten(h):
ignoring = False
for i in range (1,len(h)-2):
if ignoring == True and h[i]==10:
h.remove(10)
if ignoring == False and h[i] ==10:
ignoring = True
The basic idea of this is just to decided the first 10 in a list, keep it, continue iterating until you faced another 10, then just remove that 10 to avoid replication, I had searched around but can't seem to find any solution and that's why I have to bring it up here. Thank you
The code you listed
def ignoreten(h):
ignoring = False
for i in range (1,len(h)-2):
if ignoring == True and h[i]==10:
h.remove(10)
if ignoring == False and h[i] ==10:
ignoring = True
Will actually do almost the exact opposite of what you want. It'll iterate over h (sort of, see [1]), and if it finds 10 twice, it'll remove the first occurrence from the list. (And, if it finds 10 three times, it'll remove the first two occurrences from the list.)
Note that list.remove will:
Remove the first item from the list whose value is equal to x. It
raises a ValueError if there is no such item.
Also note that you're mutating the list you're iterating over, so there's some additional weirdness here which may be confusing you, depending on your input.
From your follow-up comment to my question, it looks like you want to remove only the second occurrence of 10, not the first and not any subsequent occurrences.
Here are a few ways:
Iterate, store index, use del
def ignoreten(h):
index = None
found_first = False
for i,v in enumerate(h):
if v == 10:
if not found_first:
found_first = True
else:
index = i
break
if index is not None:
del h[index]
A little more verbose than necessary, but explicit, safe, and modifiable without much fear.
Alternatively, you could delete inside the loop but you want to make sure you immediately break:
def ignoreten(h):
found_first = False
for i,v in enumerate(h):
if v == 10:
if not found_first:
found_first = True
else:
del h[i]
break
Collect indices of 10s, remove second
def ignoreten(h):
indices = [i for (i,v) in enumerate(h) if v == 10]
if len(indices) > 1:
del h[indices[1]] # The second index of 10 is at indices[1]
Clean, but will unnecessarily iterate past the second 10 and collect as many indices of 10s are there are. Not likely a huge issue, but worth pointing out.
Collect indices of 10s, remove second (v2, from comments)
def ignoreten(h):
indices = [i for (i,v) in enumerate(h) if v == 10]
for i in reversed(indices[1:]):
del h[i]
From your comment asking about removing all non-initial occurrences of 10, if you're looking for in-place modification of h, then you almost definitely want something like this.
The first line collects all the indices of 10 into a list.
The second line is a bit tricky, but working inside-out it:
[1:] "throws out" the first element of that list (since you want to keep the first occurrence of 10)
reversed iterates over that list backwards
del h[i] removes the values at those indices.
The reason we iterate backwards is because doing so won't invalidate the rest of our indices that we've yet to delete.
In other words, if the list h was [1, 10, 2, 10, 3, 10], our indices list would be [1, 3, 5].
In both cases we skip 1, fine.
But if we iterate forwards, once we delete 3, and our list shrinks to 5 elements, when we go to delete 5 we get an IndexError.
Even if we didn't go out of bounds to cause an IndexError, our elements would shift and we'd be deleting incorrect values.
So instead, we iterate backwards over our indices, delete 5, the list shrinks to 5 elements, and index 3 is still valid (and still 10).
With list.index
def ignoreten(h):
try:
second_ten = h.index(10, h.index(10)+1)
del h[second_ten]
except ValueError:
pass
The inner .index call finds the first occurrence, the second uses the optional begin parameter to start searching after that. Wrapped in try/except in case there are less than two occurrences.
⇒ Personally, I'd prefer these in the opposite order of how they're listed.
[1] You're iterating over a weird subset of the list with your arguments to range. You're skipping (not applying your "is 10" logic to) the first and last two elements this way.
Bonus: Walrus abuse
(don't do this)
def ignoreten(h):
x = 0
return [v for v in h if v != 10 or (x := x + 1) != 1]
(unlike the previous versions that operated on h in-place, this creates a new list without the second occurrence of 10)
But the walrus operator is contentious enough already, please don't let this code out in the wild. Really.

what's the difference between iterator and array?

I am trying to write a function that takes a sequence of numbers and determines if all the numbers are different from each other.
This was my first attempt
def differ(data):
for i in data:
print(i)
for j in data:
print(j)
if i==j:
return False
return True
print(differ([1,23,4,1,2,3,1,2]))
print(differ([1,2,3,4]))
1
1
False
1
1
False
Apparently, the for loop didn't loop over all the numbers in the data. Why did this happen?
I wrote another function using range().
def differ(data):
for i in range(1,len(data)):
print("i:",i)
for j in range(i):
print("j:",j)
if data[i]==data[j]:
return False
return True
print(differ([1,2,2,4,53]))
i: 1
j: 0
i: 2
j: 0
j: 1
False
It works, however, I don't understand why my first attempt didn't work.
The problem with your first attempt is that it will eventually compare the same number with itself.
In the sequence [1,2,3,4] the first function will start off with i=1 and j=1. They are both looking at the first number in the list causing it to fail.
The second attempt avoids this by only looking at the numbers before it. range(i) doesn't actually include i, so j can only ever be less than i meaning they will never point to the same value in the list.
I think Tim Roberts answer explains perfectly why your code is not working as you expect. I would like to complement the answer:
Answering your question
Arrays are similar to lists, one of the differences is that the former consists only of elements with the same data type.
In the other hand, iterators are objects which you can iterate through, these objects must implement at least the following 2 methods on their respective classes: __iter__() and __next__()
Cool way to do what you want in 1 line
You can achieve what you want with this:
def differ(data):
return len(set(data)) == len(data)
print(differ([1, 2, 3, 4])) # True
print(differ([1, 23, 4, 1, 2, 3, 1, 2])) # False
So basically, the magic here happens when you use set(). Sets are similar to lists, but one of the main differences is that they can't have repeated elements. By transforming the list to a set you are removing every duplicated element, so if there is a difference in the length after casting to a set, it means there was at least one duplicated element.

Number Filtration Algorithm bug

So I wrote this algorithm where given a set of integers it will remove all integers except 0 and 7 and then it will check if the remaining integers are in a certain order and then will return a boolean. Code below:
def spy_game(nums):
for i in nums:
if i != 0:
if i == 7:
continue
else:
nums.remove(i)
else:
continue
stringlist = [str(o) for o in nums]
mystring = ''.join(stringlist)
return '007' in mystring
spy_game([1,0,2,4,0,7,5])
Now the problem is that if I run
(for example) spy_game([1,0,2,4,0,7,5]) it will not return True regardless of the fact that the sequence of interest is present. After I decided to return the list per se after the filtration process, I found that all numbers except the ones in the middle got filtered out. So in this example, if I return nums it will return [0, 4, 0, 7] although the 4 should've been removed. I am aware that there are more optimal alternatives to this algorithm but I just want to understand why it doesn't work. Thank you.
Instead of modifying the list, use another list to keep track of the wanted numbers.
You should not modify the list while iterating on it.
Here's a cleaned up version
def spy_game(nums):
ans = []
for i in nums:
if i == 0 or i == 7:
ans.append(i)
stringlist = [str(o) for o in ans]
mystring = ''.join(stringlist)
return '007' in mystring
zenwraight's comment says what the problem is: in Python, you can't modify a list while iterating over it.
As for why, the Python documentation discusses this in a note on the for statement's section:
An internal counter is used to keep track of which item is used next, and this is incremented on each iteration. … This means that if the [loop body] deletes the current … item from the sequence, the next item will be skipped (since it gets the index of the current item which has already been treated).
The documentation also describes what happens when you insert an element during a loop, and suggests one possible solution (using a slice to copy the list: for i in nums[:]: ...). In your use case, that solution is likely to work fine, but it is considerably less efficient than options that don't copy the entire list.
A better solution might be to use another list comprehension:
nums = [i for i in nums if i == 0 or i == 7]

Why is my loop not following its condition?

so im working on this code for school and i dont know much about python. Can someone tell me why my loop keeps outputting invalid score when the input is part of the valid scores. So if i enter 1, it will say invalid score but it should be valid because i set the variable valid_scores= [0,1,2,3,4,5,6,7,8,9,10].
This code has to allow the user to enter 5 inputs for six different groups and then add to a list which will find the sum of the list and the avarage of the list.
Code:
valid_scores =[0,1,2,3,4,5,6,7,8,9,10]
for group in groups:
scores_for_group = []
valid_scores_loops=0
print("What do you score for group {group_name} as a player?"\
.format(group_name=group))
while valid_scores_loops < 5:
valid_scores=True
player_input =input("* ")
if player_input==valid_scores:
player1.append(player_input)
scores_for_player.append(int(player_input))
else:
if player_input!=valid_scores:
valid_scores= False
print("Invalid score!")
You're setting valid_scores=True, which overwrites your list of valid scores with a boolean value of True. Then, when you check player_input!=valid_scores, it will always return False.
What you want to do is get rid of the valid_scores=True line, cast the player_input to an integer using int(player_input), then check int(player_input) in valid_scores. This will return True if the player inputs a string that can be cast to one of the integers in valid_scores.
In your code there you are comparing a list (the valid values) with a string (the input.) This means you would have to give in a list for the statement to be correct. The right way is input in valid_inputs, which will check if any of the elements in the valid_inputs list equals the variable input. You're also assigning True to valid_inputs, which essentially deletes the list. Another error is that the player1 and scores_for_player variables aren't defined in your code. To fix your code, you'll have to cast the input to an integer, the variable storing if the input is valid must be renamed, the variables player1 and scores_for_player must be declared, and you probably should adjust your code with the fixes below.
You can also improve your code by replacing while valid_score_loops < 5 with for valid_score_loops in range(5) because you don't change valid_score_loops in loop. Another way for you to express [0,1,2,3,4,5,6,7,8,9,10] is to convert it to a tuple: (0,1,2,3,4,5,6,7,8,9,10) or express it as a range: range(0, 11).
Ranges are objects like lists and tuples that contain a sequence of numbers. The signatures of the range method are:
range(end) with start equivalent to 0 and step to 1
range(start, end) with step equivalent to 1
range(start, end, step)
It will start at start and increase by step each time, ending before end.
Example:
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(3, 5))
[3, 4]
>>> list(range(1, 6, 2))
[1, 3, 5]
There is another optimization to make to your code. If the input isn't valid, it is invalid, so you don't have to check that in your code. To fix that, you can just remove the check if the argument isn't in the valid_scores list inside the else clause.

Count the number of times each element appears in a list. Then making a conditional statement from that

write a function that takes, as an argument, a list called aList. It returns a Boolean True if the list contains each of the integers between 1 and 6 exactly once, and False otherwise.
This is homework and I thought I had it right, but it is now telling me that it isn't right. Here is my code.
def isItAStraight(aList):
count = 0
for i in set(aList):
count += 1
return aList.count(i) == 1
for some reason even if a number appears more than once it still gives true and I can't figure out why it won't give me false unless the first or last number are changed.
Each number has to occur only one time otherwise it is false.
So like take [1,2,3,4,5,6]
would be true.
But [1,2,2,3,4,5]
would be false.
Also, I can't import things like Counter or collections (though it would be so much easier to do it isn't apart of the assignment.)
The list is randomly generated from 1 to 6.
With a return inside the loop, you are only checking one value. You need to check each value. Also, instead of looping through the items of the list, you should loop through the items you're actually looking for. It would also help to make sure there are the correct number of items in the list.
def isItAStraight(aList):
if len(aList) != 6:
return False
for i in range(1, 7):
if aList.count(i) != 1:
return False
return True
But the easiest way to do this is to simply sort the list and check if it's what you're looking for:
def isItAStraight(aList):
return sorted(aList) == list(range(1, 7))
You need to be careful about what's inside the list. What you've written is a basically the same as the pseudo-code below:
let count = 0
for every unique element in aList:
Add 1 to count
if count is now 1, return true.
This will always return true if there is at least one element in aList, since you're adding 1 to count and then returning immediately.
A couple approaches to consider:
Create a 6 element list of all zeros called flags. Iterate over aList and set the corresponding element in flags to 1. If flags is all ones, then you return true.
Sort the list, then check if the first six numbers are 1, 2, 3, 4, 5, 6.

Categories