I was trying to create a reverse rail-fence cipher, when I stumbled upon an error!
Here is my code:
n=len(cipher)
strings=[]
for r in range(3,4):
lis=[[0 for i in range(n)] for i in range(r)]
x,y=0,0
for c in cipher:
try:
lis[x][y]=c
except:
for i in lis:
print(i)
print(x,y)
break
y+=2*r-3
print(y)
if y>=n:
y-=n
x+=1
print(x,y)
x,y,z=0,0,1
s=""
for i in range(n):
try:
if(lis[x][y]==0):
print(r,n)
s+=lis[x][y]
except:
for i in lis:
print(i)
print(x,y,c)
break
y+=1
if x==0:
z=1
elif x==r-1:
z=0
if z==1:
x+=1
else:
x-=1
strings.append(s)
return strings
I added in several try/except statements to determine the error. Later though, I found that if I changed a single line, line 4, to read as
lis=[['.' for i in range(n)] for i in range(r)]
The code worked flawlessly (as in didn't create any errors - it still didn't produce the right result)!
The only differences between the codes is this single line. I am using Python 37 on Windows 10.
Why does the one code work where the other doesn't?
EDIT: My input cipher is "Js s!ae saeoemiwm", which was encoded using three "rails" in a rail-fence cipher.
EDIT 2: I have a working version to reverse this cipher, but the point of this question is why does changing the 0's to '.' make a difference in any way?
The reason is this line:
s+=lis[x][y]
s is a string, so you can only concatenate other strings to it. If lis[x][y] contains 0, that's a number, not a string, so you can't concatenate it.
If you change it to:
s += str(lis[x][y])
then it will work with either initial value.
Related
i=1
if int(n)==sum(int(x)**i for x in str(n); i=i+1)
print("Disarum Number")
else:
print("Non-Disarum Number")
I expected the value of i to increase after each iteration. I wanted a short version of the following code :-
i=1
s=0
for x in str(n):
s=s+(int(x)**i)
i=i+1
The thing is, compound for is not a short version of a for loop.
It is an expression, not an instruction (even tho, in python, like in C, difference is less important, it still is).
I've recently explained that quite lengthly in an answer to a different question (not at all a duplicate question. Just answer is pertinent here too), if you wish to read more about that. Bottom line being, you can only have a value in compound for. It is not "for thing in iterable, do something", but "worth something for thing in iterable".
So you can't add a print, or a i+=1 or an instruction in this.
The good news is that expression can still have side effects. And there is one expression, made from operator := that has the side effect you want. It is both an expression (it has a value) and an instruction (it does something).
i:=12 has the value 12, and has the side effect that, now, i value is 12. You can
print(i:=12), and it prints value 12, which is value of expression i:=12, and sets i to be 12. So it is somehow the equivalent of i=12; print(i).
So in your case
i=1
print(sum(int(x)**i for x in str(n); i=i+1))
has no meaning
But
i=0
print(sum(int(x)**(i:=i+1) for x in str(n)))
has.
(Note that I initialize i to 0, not 1, because i:=i+1 worth what was i+1, not i. So i=0; print(i:=i+1) prints 1, and set i to 1. First i in your iteration is 1, as wanted.
For example
n=432
i=0
print(sum(int(x)**(i:=i+1) for x in str(n)))
Displays 21. That is 4¹+3²+2³=4+9+8=21. As expected I think.
So, your complete code turns
i=0
if int(n)==sum(int(x)**(i:=i+1) for x in str(n))
print("Disarum Number")
else:
print("Non-Disarum Number")
This should work (enumerate(iterable)) attaches indexes to the iterable).
if int(n)==sum(int(x)**i for i,x in enumerate(str(n),1)):
print("Disarum Number")
else:
print("Non-Disarum Number")
Another way to do this would be to loop on the INDEX i, and retrieve the digit with str(n)[i] :
if int(n) == sum( int(str(n)[i])**(i+1) for i in range(len(str(n)))):
...
I suggest to do this as a function form named disarium_number :
def disarium_number(number):
number=str(number)
sum_powers=0
for i in [*number]:
sum_powers=sum_powers+(int(i)**(number.index(i)+1))
if int(sum_powers)==int(number):
print("Disarium Number")
else:
print("Non-Disarium Number")
Let's give it a try:
>>> disarium_number(175)
'Disarium Number'
>>> disarium_number(225)
'Non-Disarium Number'
I'm new to a programming language and wanted to start with Python as its the recommendation of most people (as far as i see).
So, im practising on some functions to improve my understanding on loops, and basic statements etc. Though i'm not very good at it yet, i do believe that i'll improve sooner or later.
Here is an example where i'm stuck at:
def L():
List = []
TauS = []
a = 12
for i in range(1,a+1):
if a % i == 0:
List.append(i)
if a % len(List) == 0:
TauS.append(a)
print(List)
print(TauS)
L()
This is the function i want to have and the output is:
[1, 2, 3, 4, 6, 12]
[12]
As i expected.However, the problem is that i want "a" to be a variable instead of a constant.Something like:
def L():
List = []
TauS = []
for a in range(2,20):
for i in range(1,a+1):
if a % i == 0:
List.append(i)
if a % len(List) == 0:
TauS.append(a)
print(List)
print(TauS)
L()
Fails because it seems like for loop is working before the 2nd if statement (if a % len(list)) == 0: TauS.append(a)).I have also tried a "while" loop instead of a "for" loop as:
a = 2
while a <= 20:
for i in range(1,a+1):...(The rest is the same)
It would be a better idea if your help focus on the basic ideas instead of just giving the right function.
Thanks a lot from now!
Regards.
Python uses indention levels to tell whether code is in within a function, loop or condition, the general thing being that if there is a : then all the code indented underneath it is within that statement.
The problem with your code is that the second if statement is on the same indention level as the for loop rather than being on the indention level below the for loop. This means that rather than running in the for loop it runs after it.
So to fix your code all you need to do is select the if statement and press crtl + ] which is pythons keyboard shortcut for indent code section.
edit:
I think what you're asking for is to get all the factors of numbers from 2 to 19 and then print numbers where the number of factors is a factor of that number.
def L():
List = []
TauS = []
for a in range(2,20):
l=[] #this is the list for each individual number
#if i is a factor of a add it to l
for i in range(1,a+1):
if a % i == 0:
l.append(i)
#if length of l is a factor of a add a to TauS
if a % len(l) == 0:
TauS.append(a)
List.append(l)
print(List)
print(TauS)
L()
It fails because of variable scope.
Python uses indention to represent code block. So, here for loop and 2nd if condition has same indention which means they belong to same code block and can access variables defined in the same or the outer code block. "List" & "Taus" in this case.
However, Variable "a" is localize to outer for loop and can be access in the same or inner for loop and cant be access from outside. Similarly variable "i" is specific to inner for loop and cant be access outside of the loops block, not even from outer for loop.
Hope it helps...
I am wondering if there is a way that I can compare the first letter of the string to the second; then the third to the forth ... etc.
At the moment I have this:
a=0
b=0
string = "GG EZ"
if string[a:b] == string[a+1:b+1]:
print("hello")
It works but is there a more efficient way of doing it?
You can use zip to pair up the elements
s = "GGEZ"
for a, b in zip(*[iter(s)]*2):
if a==b:
print('Hello')
More on this usage of zip here
string = "GG EZ abbdff"
for i in range(0, len(string), 2):
if string[i] == string[i+1]:
print("characters match")
Create a range of every second character in the string. Iterate over the range and compare character at the index i with the following.
Edit: If your string length is an odd number you run into an out of range exception. I let you figure that one out by yourself :)
I am trying to find the index of the first letter of a sub string within the main string. The function acts exactly like the find method of python. I have created a find_chr function that gives me the index of a character in a string and I am using the find_chr to get the index of the substring.
def find_str(s,x):
i=0
if x in s:
return find_chr(s,x[i])
else:
return -1
My problem is that when I am using the string "IS GOING GOING" and substring as "ING", I am getting the index of the first "I", when I am expecting the index of the "I" of "ING". I will appreciate any input about changing the function to get the right index of the first letter of the substring.
In find_str you call find_chr(s,x[i]). This is calling find_chr with only x[i] (the ith part of the substring).
This should fix your problem
def find_chr(s,char):
i=0
step = len(char)
for j in range(len(s)+1):
ch = s[j:j+step]
if ch==char:
return (i)
break
i+=1
return -1
def find_str(s,x):
i=0
if x in s:
return find_chr(s,x)
else:
return -1
You aren't looping through the characters, you only check for i == 0 (i.e. the first character in s). You need to apply a "window" to the string, checking len(s) characters in a row:
def find_str(s, x):
if x in s: # is x present?
for i in range(len(s)): # work through string indices
if s[i:i+len(x)] == x: # does x start at current index?
return i
return -1
This should solve your problem:
def find_str(s, x):
i = 0
while i < len(s):
if s[i:i + len(x)] == x:
return i
else:
i += 1
print find_str('IS GOING GOING', 'ING')
Look up the use of the index function in strings. You will then happily replace all of that code with about 1 line.
Supplying the answer because of the following comments. Seriously though, if one is going to learn python, it is a good exercise to be aware of the methods available for an object.
>>> 'is going going'.index('ing')
5
or more generally
>>> fullstring.index(substring)
This should be marked as the correct answer because it is the simplest and most obviously correct. The complexity of the algorithms offered is way too high for this problem.
If the substring is not in the fullstring, a ValueError exception will be raised. So if you need a function, then it should return the index from a try or -1 (or None) from the except blocks.
I have written a simple python program
l=[1,2,3,0,0,1]
for i in range(0,len(l)):
if l[i]==0:
l.pop(i)
This gives me error 'list index out of range' on line if l[i]==0:
After debugging I could figure out that i is getting incremented and list is getting reduced.
However, I have loop termination condition i < len(l). Then why I am getting such error?
You are reducing the length of your list l as you iterate over it, so as you approach the end of your indices in the range statement, some of those indices are no longer valid.
It looks like what you want to do is:
l = [x for x in l if x != 0]
which will return a copy of l without any of the elements that were zero (that operation is called a list comprehension, by the way). You could even shorten that last part to just if x, since non-zero numbers evaluate to True.
There is no such thing as a loop termination condition of i < len(l), in the way you've written the code, because len(l) is precalculated before the loop, not re-evaluated on each iteration. You could write it in such a way, however:
i = 0
while i < len(l):
if l[i] == 0:
l.pop(i)
else:
i += 1
The expression len(l) is evaluated only one time, at the moment the range() builtin is evaluated. The range object constructed at that time does not change; it can't possibly know anything about the object l.
P.S. l is a lousy name for a value! It looks like the numeral 1, or the capital letter I.
You're changing the size of the list while iterating over it, which is probably not what you want and is the cause of your error.
Edit: As others have answered and commented, list comprehensions are better as a first choice and especially so in response to this question. I offered this as an alternative for that reason, and while not the best answer, it still solves the problem.
So on that note, you could also use filter, which allows you to call a function to evaluate the items in the list you don't want.
Example:
>>> l = [1,2,3,0,0,1]
>>> filter(lambda x: x > 0, l)
[1, 2, 3]
Live and learn. Simple is better, except when you need things to be complex.
What Mark Rushakoff said is true, but if you iterate in the opposite direction, it is possible to remove elements from the list in the for-loop as well. E.g.,
x = [1,2,3,0,0,1]
for i in range(len(x)-1, -1, -1):
if x[i] == 0:
x.pop(i)
It's like a tall building that falls from top to bottom: even if it is in the middle of collapse, you still can "enter" into it and visit yet-to-be-collapsed floors.
I think the best way to solve this problem is:
l = [1, 2, 3, 0, 0, 1]
while 0 in l:
l.remove(0)
Instead of iterating over list I remove 0 until there aren't any 0 in list
List comprehension will lead you to a solution.
But the right way to copy a object in python is using python module copy - Shallow and deep copy operations.
l=[1,2,3,0,0,1]
for i in range(0,len(l)):
if l[i]==0:
l.pop(i)
If instead of this,
import copy
l=[1,2,3,0,0,1]
duplicate_l = copy.copy(l)
for i in range(0,len(l)):
if l[i]==0:
m.remove(i)
l = m
Then, your own code would have worked.
But for optimization, list comprehension is a good solution.
The problem was that you attempted to modify the list you were referencing within the loop that used the list len(). When you remove the item from the list, then the new len() is calculated on the next loop.
For example, after the first run, when you removed (i) using l.pop(i), that happened successfully but on the next loop the length of the list has changed so all index numbers have been shifted. To a certain point the loop attempts to run over a shorted list throwing the error.
Doing this outside the loop works, however it would be better to build and new list by first declaring and empty list before the loop, and later within the loop append everything you want to keep to the new list.
For those of you who may have come to the same problem.
I am using python 3.3.5. The above solution of using while loop did not work for me. Even if i put print (i) after len(l) it gave me an error. I ran the same code in command line (shell)[ window that pops up when we run a function] it runs without error. What i did was calculated len(l) outside the function in main program and passed the length as a parameter. It worked. Python is weird sometimes.
I think most solutions talk here about List Comprehension, but if you'd like to perform in place deletion and keep the space complexity to O(1); The solution is:
i = 0
for j in range(len(arr)):
if (arr[j] != 0):
arr[i] = arr[j]
i +=1
arr = arr[:i]
x=[]
x = [int(i) for i in input().split()]
i = 0
while i < len(x):
print(x[i])
if(x[i]%5)==0:
del x[i]
else:
i += 1
print(*x)
Code:
while True:
n += 1
try:
DATA[n]['message']['text']
except:
key = DATA[n-1]['message']['text']
break
Console :
Traceback (most recent call last):
File "botnet.py", line 82, in <module>
key =DATA[n-1]['message']['text']
IndexError: list index out of range
I recently had a similar problem and I found that I need to decrease the list index by one.
So instead of:
if l[i]==0:
You can try:
if l[i-1]==0:
Because the list indices start at 0 and your range will go just one above that.