Longest sequence of equal numbers in python - python

I tried to generate the longest sequence of equal numbers in python, but it doesn't work
def lista_egale(lst1 = input("numbers go here ")):
l = 0
lst1 = []
maxi = -9999
prev_one = None
lmax = -9999
for current in lst1:
if prev_one == current:
l += 1
else:
l = 1
if l > lmax:
lmax = l
maxi = current
prev_one = current
print("longest sequence is ", lmax, " and ", maxi)
lista_egale()
Input:
1 2 2 2 2 3 4 5 6 2 2 2
Expected Output:
longest sequence is 4 and 2

I was going to write up the same concern about your default argument, but that would at least work correctly the first time it is called. This function does not. Everyone jumped on that common problem, and failed to notice the next line. Let's look another look at this abridged version of your code:
irrelevant = input("numbers go here ")
def lista_egale(lst1 = irrelevant):
# while it is true that your default argument is bad,
# that doesn't matter because of this next line:
lst1 = []
for current in lst1:
# unreachable code
pass
To clarify, since your reply indicates this is not clear enough, it doesn't matter what value was passed in to lst1 if you immediately overwrite it with an empty list.
(for others reading this:) Separating out what I labeled "irrelevant" is not quite identical, but I'm trying to point out that the input was overwritten.
I don't think this function should take user input or have a default argument at all. Let it be a function with one job, and just pass it the data to work on. User input can be collected elsewhere.

Based on Barmar's note, and the principle of using only unmutable default values, your code should look something more like this:
def lista_egale(inp1 = None):
if not inp1:
inp1 = input("numbers go here ")
# optionally do some error checking for nonnumerical characters here
lst1 = [int(i) for i in inp1.split(" ")]
# rest of your code here
lista_egale()
Basically, input returns a string value, and you need to convert it into a list of integers first before you start working on it.
You can swap out the list comprehension for map(int, inp1.split(" ")) as it will do the same (but you can't iterate through a map more than once unless you wrap it in a list() function first).
Secondly, avoid setting mutable default arguments as (in short) can lead to weird results when rerunning the same function multiple times.

Related

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]

Multiple inputs from one input

I'm writing a function to append an input to a list. I want it so that when you input 280 2 the list becomes ['280', '280'] instead of ['280 2'].
>>> number, factor = input().split()
280 2
>>> [number]*int(factor)
['280', '280']
Remember that concatenating a list with itself with the * operator can have unexpected results if your list contains mutable elements - but in your case it's fine.
edit:
Solution that can handle inputs without a factor:
>>> def multiply_input():
... *head, tail = input().split()
... return head*int(tail) if head else [tail]
...
>>> multiply_input()
280 3
['280', '280', '280']
>>> multiply_input()
280
['280']
Add error checking as needed (for example for empty inputs) depending on your use case.
You can handle the case with unspecified number of repetitions by extending the parsed input with a list containing 1. You can then slice the list to leave the first 2 items (in case the number of repetitions was provided, that [1] will be discarded)
number, rep = (input().split() + [1])[:2]
[number] * int(rep)
from itertools import repeat
mes=input("please write your number and repetitions:").split()
listt= []
listt.extend(repeat(int(mes[0]), int(mes[1]))
#repeat(object [,times]) -> create an iterator which returns the object
#for the specified number of times. If not specified, returns the object
#endlessly.
This code provides exception handling against no second number being provided with in put.
def addtolist():
number = input("Enter number: ")
try:
factor = input("Enter factor: ")
except SyntaxError:
factor = 1
for i in range(factor):
listname.append(number)

Input on same line in Python

I have a problem with an output in Python
I'm trying to have an input in the form
2
1 10
2 20
2 being the number of tests and each line representing the number used by the function
To be able to have a single output, I'm storing the values in a tab in Python. The problem is that my tab doesn't have the correct values. Using the values given before, I have the following result when looking into the tab
1 2 20 5
Here's my code, thanks :
nbTest = input()
Tab = range(1,2*nbTest + 2)
for i in range(1,nbTest + 1):
a,b = map(int,sys.stdin.readline().split())
Tab[i] = a
Tab[i+1] = b
i = i + 1
print Tab[1], Tab[2], Tab[3], Tab[4]
First you don't have to "initialize" a list in python, so
Tab = range(1,2*nbTest + 2)
is unnecessary.
Second: the line
i = i + 1
doesn't do anything since the variable i is reassigned immediately afterwards.
Third: You should append tuples of values to your output list like this:
nbTest = int(raw_input())
inputs = []
for i in range(0,nbTest):
a,b = map(int, raw_input().split())
inputs.append((a,b))
print nbTest
for el in inputs:
print el[0], el[1]
Your should not modify the loop variable i inside the loop.
The line i = i + 1 does nothing since i is reassigned by the for loop, so you overwrite the Tab[i+1] of the previous iteration with Tab[i]. If you want i to increase by 2 at each iteration use range(1, nbTest+1, 2) instead.
Also note that you don't have to initialise the size of your list, you can simply do Tab=list(), and then use Tab += [a,b]
Also, pythons arrays start at 0, so your range must be start from 0 as well. Otherwise the first slot of the list will not be replaced.
Note If you are using python3, you must do Tab = list(range(1, 2*nbTest+2)) if you want Tab to be a list.

How to change for-loop iterator variable in the loop in Python?

I want to know if is it possible to change the value of the iterator in its for-loop?
For example I want to write a program to calculate prime factor of a number in the below way :
def primeFactors(number):
for i in range(2,number+1):
if (number%i==0)
print(i,end=',')
number=number/i
i=i-1 #to check that factor again!
My question : Is it possible to change the last two line in a way that when I change i and number in the if block, their value change in the for loop!
Update: Defining the iterator as a global variable, could help me? Why?
Short answer (like Daniel Roseman's): No
Long answer: No, but this does what you want:
def redo_range(start, end):
while start < end:
start += 1
redo = (yield start)
if redo:
start -= 2
redone_5 = False
r = redo_range(2, 10)
for i in r:
print(i)
if i == 5 and not redone_5:
r.send(True)
redone_5 = True
Output:
3
4
5
5
6
7
8
9
10
As you can see, 5 gets repeated. It used a generator function which allows the last value of the index variable to be repeated. There are simpler methods (while loops, list of values to check, etc.) but this one matches you code the closest.
No.
Python's for loop is like other languages' foreach loops. Your i variable is not a counter, it is the value of each element in a list, in this case the list of numbers between 2 and number+1. Even if you changed the value, that would not change what was the next element in that list.
The standard way of dealing with this is to completely exhaust the divisions by i in the body of the for loop itself:
def primeFactors(number):
for i in range(2,number+1):
while number % i == 0:
print(i, end=',')
number /= i
It's slightly more efficient to do the division and remainder in one step:
def primeFactors(number):
for i in range(2, number+1):
while True:
q, r = divmod(number, i)
if r != 0:
break
print(i, end=',')
number = q
The only way to change the next value yielded is to somehow tell the iterable what the next value to yield should be. With a lot of standard iterables, this isn't possible. however, you can do it with a specially coded generator:
def crazy_iter(iterable):
iterable = iter(iterable)
for item in iterable:
sent = yield item
if sent is not None:
yield None # Return value of `iterable.send(...)`
yield sent
num = 10
iterable = crazy_iter(range(2, 11))
for i in iterable:
if not num%i:
print i
num /= i
if i > 2:
iterable.send(i-1)
I would definitely not argue that this is easier to read than the equivalent while loop, but it does demonstrate sending stuff to a generator which may gain your team points at your next local programming trivia night.
It is not possible the way you are doing it. The for loop variable can be changed inside each loop iteration, like this:
for a in range (1, 6):
print a
a = a + 1
print a
print
The resulting output is:
1
2
2
3
3
4
4
5
5
6
It does get modified inside each for loop iteration.
The reason for the behavior displayed by Python's for loop is that, at the beginning of each iteration, the for loop variable is assinged the next unused value from the specified iterator. Therefore, whatever changes you make to the for loop variable get effectively destroyed at the beginning of each iteration.
To achieve what I think you may be needing, you should probably use a while loop, providing your own counter variable, your own increment code and any special case modifications for it you may need inside your loop. Example:
a = 1
while a <= 5:
print a
if a == 3:
a = a + 1
a = a + 1
print a
print
The resulting output is:
1
2
2
3
3
5
5
6
Yes, we can only if we dont change the reference of the object that we are using. If we can edit the number by accessing the reference of number variable, then what you asked is possible.
A simple example:
a=[1,2,3]
a=a+[4]==>here, a new object is created which plots to different address.
a+=[4]==>here , the same object is getting updated which give us the desired result.
number=10
list1=list(range(2,number+1))
# list1
for i in list1:
print(list1,i)
if (number%i==0):
print(i,end=',')
number=number//i #we can simply replace it with number//=i to edit the number without changing the reference or without creating a new object.
try:
[list1.pop() for i in range(10,0,-1) if(i>number)]
#here pop() method is working on the same object which list created by number refers. so, we can able to change the iterable in the forloop.
except:
continue
i=i-1 #to check that factor again!

Fragment words combination [duplicate]

This question already has answers here:
for loop to iterate through words
(2 answers)
Closed 8 years ago.
I would like to do a conjunction of words where
EG1
input [jason, sonny, nyorth]
output [jason, sonny, nyorth, jasonnyorth]
EG2
Sample input: [aw, was,poq, qo, soo]
Output [aw, was, poq, qo, soo, awasoo, poqo]
EG3
input: [keyboard, ardjimmy]
output:[keyboard, ardjimmy, keyboardjimmy]
My code is shown below. There's 3 things that I would like to improve on.
I don't want to use check and maxNum. I feel like there will be some cases where the 2 variables will cause an error. It also feels like bad programming.
My function is not doing exactly what it meant to do. Meaning, if I am given [abb, bba] my function outputs [abb, bba, abba, bbabb] instead of [abb, bba, abbba, bbabb]
Lastly, my function does not recurse itself. For example: given the input [ab, ba] the output should be
[ab, ba, aba, bab] then it should recurse and become [ab, ba, aba, bab, abab, baba] it will keep going to infinity. When I encounter an infinite loop I should handle that but I'm not even close to solving this problem as of yet.
For 1) I am not sure how I am going to replace it with. For 3) I haven't even solve 1) and 2) I don't want to make my function recurse itself. It will get all sorts of weird errors. I want to keep my post clean for revision. Also I will bump into unnecessary duplicates if I do recursion. I would appreciate if someone can enlighten me on 3). For now my priority is 1) and 2).
#testing = ["jimmy", "myolita", "paizuri", "mybaby"]
testing = ["ca","abc"]
testing1 = ["mybaby", "myolita", "jimmy", "paizuri"]
def frags(strings):
check = 1
maxNum = 0
length = len(strings)
for x in range(0,length):
for y in range(0,length):
if x != y:
for i in range(0,len(strings[y])):
if strings[x].find(strings[y][:i]) > maxNum:
check = 0
maxNum = strings[x].find(strings[y][:i])
if check == 0:
toReturn = strings[x][:maxNum] + strings[y]
strings.append(toReturn)
check = 1
maxNum = 0
return strings
print(testing)
print(frags(testing))
print(" ")
print(testing1)
print(frags(testing1))
For the recursion issue you can go brute force and start your own object instead of getting your pointer associated with the original list, i.e.
list_a = ["cat", "dog", "mouse"]
new_list = [i for i in list_a]
or
new_list = []
new_list.append(list_a[0])
...
newlist.append(list_a[0] + list_a[2])

Categories