Python loop doesn't update variable? - python

I'm trying to make a loop that turn a function (like f(x)=(2x+3)(2x-3)) into a better format for editing, simply by adding a '+' before numbers (it would become f(x)=(+2x+3)(+2x-3)). The problem is that in the loop, after I insert a new char in the middle of the string, the string doesn't update, so when the loop goes on and I try to access a certain index of the function string, the char isn't correct.
def rewriteFunction(function):
for i, c in enumerate(function):
newFunction += c
if(str(c).isdigit()):
if not(i == 0):
if not(Sign.isSign(function[i - 1])):
function = function[:i] + "+" + function[i:]
If possible, could you answer me by sending the exact (corrected) code, without modifying it too much, of course if that's the right method to do that. Thanks in advance!!

In one line you store your updated data in the variable newFunction, but in another you store your updates back into function. For consistency, let's never change function and apply all of our updates to newFunction.
You never initialize newFunction.
You never explicitly return anything from rewriteFunction().
Try this:
def rewriteFunction(function):
newFunction = ''
for i, c in enumerate(function):
if(str(c).isdigit()):
if not(i == 0):
if not(function[i - 1] in '+-'):
newFunction += '+'
newFunction += c
return newFunction
assert rewriteFunction('f(x)=(2x+3)(2x-3)') == 'f(x)=(+2x+3)(+2x-3)'

If your solutions isn't bound to using loops, you may give a try to regular expressions to simplify things:
>>> import re
>>> s = 'f(x)=(2x+3)(2x-3))'
>>> re.sub(r'\b(?<![+-])(\d+)', r'+\1', s)
'f(x)=(+2x+3)(+2x-3))'
Feel free to ask any questions about the solution.

Related

python build a list recursively

Let's say I have a string
S = "qwertyu"
And I want to build a list using recursion so the list looks like
L = [u, y, t, r, e, w, q]
I tried to write code like this:
def rec (S):
if len(S) > 0:
return [S[-1]].append(rec(S[0:-1]))
Ideally I want to append the last element of a shrinking string until it reaches 0
but all I got as an output is None
I know I'm not doing it right, and I have absolutely no idea what to return when the length of S reaches 0, please show me how I can make this work
(sorry the answer has to use recursion, otherwise it won't bother me)
Thank you very much!!!
There are many simpler ways than using recursion, but here's one recursive way to do it:
def rec (S):
if not S:
return []
else:
temp = list(S[-1])
temp.extend(rec(S[:-1]))
return temp
EDIT:
Notice that the base case ensures that function also works with an empty string. I had to use temp, because you cannot return list(S[-1]).extend(rec(S[:-1])) due to it being a NoneType (it's a method call rather than an object). For the same reason you cannot assign to a variable (hence the two separate lines with temp). A workaround would be to use + to concatenate the two lists, like suggested in Aryerez's answer (however, I'd suggest against his advice to try to impress people with convoluted one liners):
def rec (S):
if not S:
return []
else:
return list(S[-1]) + rec(S[:-1])
In fact using + could be more efficient (although the improvement would most likely be negligible), see answers to this SO question for more details.
This is the simplest solution:
def rec(S):
if len(S) == 1:
return S
return S[-1] + rec(S[:-1])
Or in one-line, if you really want to impress someone :)
def rec(S):
return S if len(S) == 1 else S[-1] + rec(S[:-1])
Since append mutates the list, this is a bit difficult to express recursively. One way you could do this is by using a separate inner function that passes on the current L to the next recursive call.
def rec(S):
def go(S, L):
if len(S) > 0:
L.append(S[-1])
return go(S[0:-1], L)
else:
return L
return go(S, [])
L = [i for i in S[::-1]]
It should work.

Function to reverse text characters

I am trying to make a reverse function which takes an input (text) and outputs the reversed version. So "Polar" would print raloP.
def reverse(text):
list = []
text = str(text)
x = len(text) - 1
list.append("T" * x)
for i in text:
list.insert(x, i)
x -= 1
print "".join(list)
reverse("Something")
As others have mentioned, Python already provides a couple of ways to reverse a string. The simple way is to use extended slicing: s[::-1] creates a reversed version of string s. Another way is to use the reversed function: ''.join(reversed(s)). But I guess it can be instructive to try implementing it for yourself.
There are several problems with your code.
Firstly,
list = []
You shouldn't use list as a variable name because that shadows the built-in list type. It won't hurt here, but it makes the code confusing, and if you did try to use list() later on in the function it would raise an exception with a cryptic error message.
text = str(text)
is redundant. text is already a string. str(text) returns the original string object, so it doesn't hurt anything, but it's still pointless.
x = len(text) - 1
list.append("T" * x)
You have an off-by-one error here. You really want to fill the list with as many items as are in the original string, this is short by one. Also, this code appends the string as a single item to the list, not as x separate items of one char each.
list.insert(x, i)
The .insert method inserts new items into a list, the subsequent items after the insertion point get moved up to make room. We don't want that, we just want to overwrite the current item at the x position, and we can do that by indexing.
When your code doesn't behave the way you expect it to, it's a Good Idea to add print statements at strategic places to make sure that variables have the value that they're supposed to have. That makes it much easier to find where things are going wrong.
Anyway, here's a repaired version of your code.
def reverse(text):
lst = []
x = len(text)
lst.extend("T" * x)
for i in text:
x -= 1
lst[x] = i
print "".join(lst)
reverse("Something")
output
gnihtemoS
Here's an alternative approach, showing how to do it with .insert:
def reverse(text):
lst = []
for i in text:
lst.insert(0, i)
print "".join(lst)
Finally, instead of using a list we could use string concatenation. However, this approach is less efficient, especially with huge strings, but in modern versions of Python it's not as inefficient as it once was, as the str type has been optimised to handle this fairly common operation.
def reverse(text):
s = ''
for i in text:
s = i + s
print s
BTW, you really should be learning Python 3, Python 2 reaches its official End Of Life in 2020.
You can try :
def reverse(text):
return text[::-1]
print(reverse("Something")) # python 3
print reverse("Something") # python 2
Easier way to do so:
def reverse(text):
rev = ""
i = len(text) - 1
while i > -1:
rev += text[i]
i = i - 1
return rev
print(reverse("Something"))
result: gnihtemoS
You could simply do
print "something"[::-1]

Using for loop to insert character between elements of string

def main():
string = raw_input("string:")
pattern = raw_input("pattern:")
end = len(string)
insertPattern(string,pattern)
def insertPattern(string,pattern):
end= len(string)-1
print "Iterative:",
for x in range(end):
if x == end:
print string[x]
if x < end:
print string[x]+pattern,
main()
I'd like this to output
Instead it's outputting
How would I modify the code to fix this? Assignment requires that I do this without lists or join.
You've got three problems here.
First, the reason you're getting that Iterative: at the beginning is because you explicitly asked for it with this line:
print "Iterative:",
Just take it out.
The reason you're getting spaces after each * is a bit trickier. The print statement's "magic comma" always prints a space. There's no way around that. So, what you have to do is not use the print statement's magic comma.
There are a few options:
Use the more-powerful print function from Python 3.x, which you can borrow in 2.7 with a __future__ statement. You can pass any separator you want to replace the space, even the empty string.
Use sys.stdout.write instead of print; that way you get neither newlines nor spaces unless you write them explicitly.
Build up the string as you go along, and then print the whole thing at the end.
The last one is the most general solution (and also leads to lots of other useful possibilities, like returning or storing the built-up string), so I'll show that:
def insertPattern(string,pattern):
result = ''
end= len(string)-1
for x in range(end):
if x == end:
result += string[x]
if x < end:
result += string[x]+pattern
print result
Finally, the extra * at the end is because x == end can never be true. range(end) gives you all the numbers up to, but not including end.
What you probably wanted was end = len(string), and then if x == end-1.
But you can simplify this quite a bit. The only reason you need x is to get string[x], and to distinguish either the first or last value from the others (so you know not to add an extra * either before the first or after the last). You can solve the last one with a flag, or by just treating the first one special. And then, you can just iterate over string itself, instead of over its indices:
def insertPattern(string,pattern):
result = string[0]
for ch in string[1:]:
result += pattern + ch
print result
And once you've done that, you may realize that this is almost identical to what the str.join method does, so you can just use that:
def insertPattern(string,pattern):
print pattern.join(string)

Index Error in python

I am new to programming, and I am trying to write a Vigenère Encryption Cipher using python. The idea is very simple and so is my function, however in this line:
( if((BinKey[i] == 'b')or(BinKey[i+1] == 'b')): )
It seems that I have an index problem, and I can't figure out how to fix it.
The error message is:
IndexError: string index out of range
I tried to replace the i+1 index by another variable equal to i+1, since I thought that maybe python is re-incrementing the i, but it still won't work.
So my questions are:
How to fix the problem, and what have I done wrong?
Looking at my code, what can I learn to improve my programming skills?
I want to build a simple interface to my program (which will contain all the encryption ciphers), and all I came up with from Google is pyqt, but it just seems too much work for a very simple interface, so is there a simpler way to build an interface? (I am working with Eclipse Indigo and pydev with Python3.x)
The Vigenère Encryption function (which contains the line that causes the problem) is:
def Viegner_Encyption_Cipher(Key,String):
EncryptedMessage = ""
i = 0
j = 0
BinKey = Bin_It(Key)
BinString = Bin_It(String)
BinKeyLengh = len(BinKey)
BinStringLengh = len(BinString)
while ((BinKeyLengh > i) and (BinStringLengh > j)):
if((BinKey[i] == 'b')or(BinKey[i+1] == 'b')):
EncryptedMessage = EncryptedMessage + BinKey[i]
else:
EncryptedMessage = EncryptedMessage + Xor(BinKey[i],BinString[j])
i = i + 1
j = j + 1
if (i == BinKeyLengh):
i = i+j
return EncryptedMessage
This is the Bin_It function:
def Bin_It(String):
TheBin = ""
for Charactere in String:
TheBin = TheBin + bin(ord(Charactere))
return TheBin
And finally this is the Xor function:
def Xor(a,b):
xor = (int(a) and not int(b)) or (not int(a) and int(b))
if xor:
return chr(1)
else:
return chr(0)
In your while condition, you are ensuring that i < len(BinKey). This means that BinKey[i] will be valid, but BinKey[i+1] will not be valid at the last iteration of your loop, as you will then be accessing BinKey[len(BinKey)], which is one past the end of your string. Strings in python start at 0 and end at len-1 inclusive.
To avoid this, you can update your loop criterion to be
while BinKeyLength > i+1 and ...:
You can either change
while ((BinKeyLengh > i) and (BinStringLengh > j)):
to
while ((BinKeyLengh > i-1) and (BinStringLengh > j)):
or change
if((BinKey[i] == 'b')or(BinKey[i+1] == 'b')):
to
if((BinKey[i] == 'b') or (BinKeyLengh > i-1 and BinKey[i+1] == 'b')):
This will avoid trying to go into BinKey[BinKeyLength], which is out of scope.
Looking at my code, what can I learn to improve my programming skills?
Looping over an index is not idiomatic Python. It's better to loop over the elements of an iterator where possible. After all, that's usually what you're interested in: for i in... is often followed by my_list[i].
In this example, you should use the built-in function zip (or itertools.izip if your code is lazy, though this isn't necessary in Python 3), which gives you pairs of values from two or more iterators, and stops when the shortest one is exhausted.
for key_char, string_char in zip(BinKey, BinString): # takes values sequentially from
# BinKey and BinString
# and binds them to the names
# key_char and string_char
# do processing on key_char and string_char
If you really must do a while loop on an index, then put the test the other way around so it's clearer what you're doing. Compare
while len(BinKey) > i and len(BinString) > j: # this looks like len(BinKey) and
# len(BinString) are varying and you're
# comparing them to static variables i and j
with
while i < len(BinKey) and j < len(BinString): # this looks like you're varying i and j
# and comparing them to len(BinKey) and len(BinString)
Which better communicates the purpose of the loop?
Finally, the clause
if (i == BinKeyLengh):
i = i+j
doesn't seem to do anything. If i == BinKeyLength then the while loop will stop in a moment anyway.
I think your error, as Python interpreter says, is that you accessing invalid array positions.
In order to solve this, unlike it's being said, you should change your code to
while (BinKeyLength > i+2 and ...):
This is because in the last step, BinKeyLength = i+2, then i+1 is BinKeyLength-1, which is the last position of your array.
Concerning your programming skills I recommend you two things:
Be the code. Sounds mystic but the most important thing that is missing here is figuring out which indices numbers are used.
As it has been said by rubik, follow some style guides like PEP8 style guide.

How do I increment a counter inside a while test of Python

How would you translate the following Java idiom to Python?
Comparable[] a, int lo, int hi;
int i = lo, j = hi+1;
Comparable v = a[lo];
while (a[++i] < v) if (i == hi) break;
My problem is that in the while test I cannot have a ++i or i += 1.
The problem you can't do that way in Python is a restriction of Python syntax. Let's get what does while look like from documentation:
while_stmt ::= "while" expression ":" suite
["else" ":" suite]
As You can see you must put expression before ":", while x += 1 is a statement (and statements doesn't return any value so cannot be used as a condition).
Here's what does this code look like in Python:
i += 1
while a[i] < v:
if i == hi:
break
i += 1
Though it works, it's most likely not a Python way to solve your problem. When you have a collection and you want to use indices you have to look forward to redesigning your code using for loop and enumerate built-in function.
P.S.
Anyway, direct code porting between languages with absolutely different philosophies is not a good way to go.
The Java code sets i to the index of either the first element >= a[lo], or to hi, whichever appears first. So:
v = a[lo]
for i in range(lo+1, hi+1):
if a[i] >= v:
break
If you want to iterate over all the objects in a list or any "iterable" thing, use "for item in list". If you need a counter as well, use enumerate. If you want a range of numbers use range or xrange.
But sometimes you really do want a loop with a counter that just goes up which you're going to break out of with break or return, just like in the original poster's example.
For these occasions I define a simple generator just to make sure I can't forget to increment the counter.
def forever(start=0):
count = start
while True:
yield count
count += 1
Then you can just write things like:
for count in forever():
if do_something() == some_value:
break
return count
The list class has a built-in method that does this kind of searching, but for whatever reason it only compares for equality. Of course, we can hack that:
class hax:
def __init__(self, value): self.value = value
def __eq__(self, other): return other >= self.value
a.index(hax(a[lo]), lo + 1, hi + 1)
... but please don't :)
Anyway, not only should you not be trying to port the code directly, as #Rostyslav suggests - you shouldn't really be trying to port the problem directly. There's something very strange about a Python program that uses lists in a way that would allow a problem like this to come up.

Categories

Resources