How to increment and decrement with a string Python? - python

Hope you all are doing well in these times.
here's my code:
def ab(n):
first = 0
last = -1
endprod = n[first] + n[last]
endprod2 = n[first+1] + n[last-1]
endprod3 = n[first+2] + n[last-2]
endprod4 = n[first+3] + n[last-3]
endprod5 = n[first+4] + n[last-4]
endprod100 = endprod[::-1] + endprod2[::-1] + endprod3[::-1]+ endprod4[::-1]+ endprod5[::-1]
return endprod100
I was able do to it, however mine isn't a loop. Is there a way to convert my code into a for loop. So, increment by 1 and decrement by 1.
Thanks,

Try this:
def ab(n):
result = ''
for j in range(len(n) // 2):
result += n[-j-1] + n[j]
if len(n) % 2 == 1:
result += n[len(n) // 2]
return result
You also need the part
if len(n) % 2 == 1:
result += n[len(n) // 2]
because your input string might have an odd number of characters
Examples:
>>> ab('0123456789')
'9081726354'
>>> ab('01234956789')
'90817263549'
If you want to reuse your original logic:
def ab(n):
result = ''
first = 0
last = -1
for j in range(len(n) // 2):
result += n[last-j] + n[first+j]
if len(n) % 2 == 1:
result += n[len(n) // 2]
return result

You could also recurse it:
def ab(s):
if len(s)>2:
return s[-1]+s[0]+ab(s[1:-1])
else:
return s
But the last part of Riccardo's answer fits your question more closely.

you need to split your string for your loop, means first you broke your string to half then build your string, you could use zip to iterate over multiple iteratable. something like this:
def ab(s):
out = ""
for v0,v1 in zip(s[:len(s)//2 -1 :-1], s[:len(s)//2 ]):
out += v0 + v1
return out
the better version you should write without loop.
like this:
out = "".join(map(lambda x: "".join(x), zip(s[:len(s)//2 -1 :-1], s[:len(s)//2 ])))

There are already a lot of good answers that are probably clearer, easier to read and much better suited for learning purposes than what I'm about to write. BUT... something I wanted to bring up (maybe just telling myself, because I tend to forget it) is that sometimes destroying the original argument can facilitate this sort of things.
In this case, you could keep "poping" left and right, shrinking your string until you exhaust it
def swap_destr(my_str):
result = ""
while len(my_str) > 1:
result += my_str[-1] # Last char
result += my_str[0] # First char
my_str = my_str[1:-1] # Shrink it!
return result + my_str # + my_str just in case there 1 char left
print(swap_destr("0123456789"))
print(swap_destr("123456789"))
# Outputs:
# 9081726354
# 918273645
This is also a good problem to see (and play with) recursion:
def swap_recur(my_str):
if len(my_str) <= 1:
return my_str
return my_str[-1] + my_str[0] + swap_recur(my_str[1:-1])
print(swap_recur("0123456789"))
print(swap_recur("123456789"))
# Outputs:
# 9081726354
# 918273645

Related

Identifying the recursion conditions for Python String

I have this string 0123456789 and I want to use recursion to create a method that returns
'09182736455463728190'
So basically the above says that first I get the first num from the left and then the first from the right, and add them to the string, then I get the second from the left and the second from the right, etc.
When I reach the middle, I start adding to the final string the values of the initial string, but now int the opposite order. So 546372, etc. So Starting from the edges I add first the most left and then the most right element.
Starting from the middle and moving to the edges, I favour the right side element first.
I cannot come up with the recursion relationship
Here it is. The base case is when the final string length is twice the original string length. This signals the end of recursion and returns the final string. The recursive step consists of advancing the start index forward and the end index backward on the original string:
def transform_sequence(original, final = '', start_index = 0, end_index = -1):
#base case:
if len(final) == 2* len(original):
return final
#recursion
final += original[start_index] + original[end_index]
return transform_sequence(original, final, start_index + 1, end_index - 1)
print(transform_sequence('0123456789'))
#output: 09182736455463728190
If you want to use recursion to handle this, you'd better try to solve it from top-down like this
def rec(nums):
if len(nums) == 0:
return ''
elif len(nums) == 1:
return nums * 2
else:
first = nums[0]
last = nums[-1]
return ''.join([first, last, rec(nums[1:-1]), last, first])
if __name__ == '__main__':
nums = '0123456789'
print(rec(nums)) # 09182736455463728190
nums = '012'
print(rec(nums)) # 021120
nums = '0'
print(rec(nums)) # 00
Fun problem and great way to learn about recursion. We can use inductive reasoning to ensure our program is valid for all input strings -
if the input s is empty, return the empty result
(inductive) s is not empty. get the first and last characters, a and b, and prepend/append them to the result of the sub-problem, s[1:-1]
We can easily encode this in python -
def first_and_last(s):
return (s[0], s[-1])
def puzzle(s):
if not s:
return "" # 1
else:
(a,b) = first_and_last(s) # 2
return a + b + puzzle(s[1:-1]) + b + a
We can see it working below -
print(puzzle("0123456789"))
print(puzzle("12345"))
09182736455463728190
152433334251
When then puzzle input is an odd length, notice how the middle character is repeated four times. When we reach the sub-problem puzzle("3"), a=3 and b=3, and the result of a + b + puzzle("") + b + a is 3333.
If you wish to handle this situation differently, we could modify puzzle
def puzzle(s):
if len(s) < 2: # <- if input is empty or singleton string
return s # <- return s
else:
(a,b) = first_and_last(s)
return a + b + puzzle(s[1:-1]) + b + a
print(puzzle("0123456789"))
print(puzzle("12345"))
09182736455463728190
152434251 # <-
The nice answer from Menglong Li presents another option for dealing with puzzle inputs of an odd length. I encourage you to see it :D
I'd take a different approach to this problem and assume the argument is a sequence rather than a str. This simplifies our logic, allowing us to pass str to the initial call, but the recursive calls can pass list:
def puzzle(sequence):
if len(sequence) > 1:
first, *middle, last = sequence
return first + last + puzzle(middle) + last + first
return sequence[0] if sequence else ""
if __name__ == '__main__':
print(puzzle("0123456789"))
print(puzzle("12345"))
print(puzzle("012"))
print(puzzle("0"))
OUTPUT
> python3 test.py
09182736455463728190
152434251
02120
0
>
If you approach this as processing the first character (placing it at the beginning and end) and recursing with the inverted remainder of the string then your exit condition will be an empty string:
def mirror(S):
return "" if not S else S[0]+mirror(S[:0:-1])+S[0]
print(mirror("0123456789")) # 09182736455463728190
If you want to avoid generating new (inverted) strings on every recursion, you could also implement it using an index that you carry along and map to alternating start/end relative positions:
def mirror(S,i=0):
j = (i+1)//2 * (-1)**i
return S[j]+mirror(S,i+1)+S[j] if i<len(S) else ""

If, else return else value even when the condition is true, inside a for loop

Here is the function i defined:
def count_longest(field, data):
l = len(field)
count = 0
final = 0
n = len(data)
for i in range(n):
count = 0
if data[i:i + l] is field:
while data[i - l: i] == data[i:i + l]:
count = count + 1
i = i + 1
else:
print("OK")
if final == 0 or count >= final:
final = count
return final
a = input("Enter the field - ")
b = input("Enter the data - ")
print(count_longest(a, b))
It works in some cases and gives incorrect output in most cases. I checked by printing the strings being compared, and even after matching the requirement, the loop results in "OK" which is to be printed when the condition is not true! I don't get it! Taking the simplest example, if i enter 'as', when prompted for field, and 'asdf', when prompted for data, i should get count = 1, as the longest iteration of the substring 'as' is once in the string 'asdf'. But i still get final as 0 at the end of the program. I added the else statement just to check the if the condition was being satisfied, but the program printed 'OK', therefore informing that the if condition has not been satisfied. While in the beginning itself, data[0 : 0 + 2] is equal to 'as', 2 being length of the "field".
There are a few things I notice when looking at your code.
First, use == rather than is to test for equality. The is operator checks if the left and right are referring to the very same object, whereas you want to properly compare them.
The following code shows that even numerical results that are equal might not be one and the same Python object:
print(2 ** 31 is 2 ** 30 + 2 ** 30) # <- False
print(2 ** 31 == 2 ** 30 + 2 ** 30) # <- True
(note: the first expression could either be False or True—depending on your Python interpreter).
Second, the while-loop looks rather suspicious. If you know you have found your sequence "as" at position i, you are repeating the while-loop as long as it is the same as in position i-1—which is probably something else, though. So, a better way to do the while-loop might be like so:
while data[i: i + l] == field:
count = count + 1
i = i + l # <- increase by l (length of field) !
Finally, something that might be surprising: changing the variable i inside the while-loop has no effect on the for-loop. That is, in the following example, the output will still be 0, 1, 2, 3, ..., 9, although it looks like it should skip every other element.
for i in range(10):
print(i)
i += 1
It does not effect the outcome of the function, but when debugging you might observe that the function seems to go backward after having found a run and go through parts of it again, resulting in additional "OK"s printed out.
UPDATE: Here is the complete function according to my remarks above:
def count_longest(field, data):
l = len(field)
count = 0
final = 0
n = len(data)
for i in range(n):
count = 0
while data[i: i + l] == field:
count = count + 1
i = i + l
if count >= final:
final = count
return final
Note that I made two additional simplifications. With my changes, you end up with an if and while that share the same condition, i.e:
if data[i:i+1] == field:
while data[i:i+1] == field:
...
In that case, the if is superfluous since it is already included in the condition of while.
Secondly, the condition if final == 0 or count >= final: can be simplified to just if count >= final:.

Without using built-in functions, a function should reverse a string without changing the '$' position

I need a Python function which gives reversed string with the following conditions.
$ position should not change in the reversed string.
Should not use Python built-in functions.
Function should be an efficient one.
Example : 'pytho$n'
Result : 'nohty$p'
I have already tried with this code:
list = "$asdasdas"
list1 = []
position = ''
for index, i in enumerate(list):
if i == '$':
position = index
elif i != '$':
list1.append(i)
reverse = []
for index, j in enumerate( list1[::-1] ):
if index == position:
reverse.append( '$' )
reverse.append(j)
print reverse
Thanks in advance.
Recognise that it's a variation on the partitioning step of the Quicksort algorithm, using two pointers (array indices) thus:
data = list("foo$barbaz$$")
i, j = 0, len(data) - 1
while i < j:
while i < j and data[i] == "$": i += 1
while i < j and data[j] == "$": j -= 1
data[i], data[j] = data[j], data[i]
i, j = i + 1, j - 1
"".join(data)
'zab$raboof$$'
P.S. it's a travesty to write this in Python!
A Pythonic solution could look like this:
def merge(template, data):
for c in template:
yield c if c == "$" else next(data)
data = "foo$barbaz$$"
"".join(merge(data, reversed([c for c in data if c != "$"])))
'zab$raboof$$'
Wrote this without using any inbuilt functions. Hope it fulfils your criteria -
string = "zytho$n"
def reverse(string):
string_new = string[::-1]
i = 0
position = 0
position_new = 0
for char in string:
if char=="$":
position = i
break
else:
i = i + 1
j = 0
for char in string_new:
if char=="$":
position_new = i
break
else:
j = j + 1
final_string = string_new[:position_new]+string_new[position_new+1:position+1]+"$"+string_new[position+1:]
return(final_string)
string_new = reverse(string)
print(string_new)
The output of this is-
nohty$x
To explain the code to you, first I used [::-1], which is just taking the last position of the string and moving forward so as to reverse the string. Then I found the position of the $ in both the new and the old string. I found the position in the form of an array, in case you have more than one $ present. However, I took for granted that you have just one $ present, and so took the [0] index of the array. Next I stitched back the string using four things - The part of the new string upto the $ sign, the part of the new string from after the dollar sign to the position of the $ sign in the old string, then the $ sign and after that the rest of the new string.

Finding if string is concatenation of others

I am working on a problem where one must determine if a string is a concatenation of other string (these strings can be repeated in the concatenated strings). I am using backtracking to be as efficient as possible. If the string is a concatenation, it will print the strings it is a concatenation of. If not, it will print NOT POSSIBLE. Here is my python code:
# note: strList has to have been sorted
def findFirstSubstr(strList, substr, start = 0):
index = start
if (index >= len(strList)):
return -1
while (strList[index][:len(substr)] != substr):
index += 1
if (index >= len(strList)):
return -1
return index
def findPossibilities(stringConcat, stringList):
stringList.sort()
i = 0
index = 0
substr = ''
resultDeque = []
indexStack = []
while (i < len(stringConcat)):
substr += stringConcat[i]
index = findFirstSubstr(stringList, substr, index)
if (index < 0):
if (len(resultDeque) == 0):
return 'NOT POSSIBLE'
else:
i -= len(resultDeque.pop())
index = indexStack.pop() + 1
substr = ''
continue
elif (stringList[index] == substr):
resultDeque.append(stringList[index])
indexStack.append(index)
index = 0
substr = ''
i += 1
return ' '.join(resultDeque)
I keep failing the last half of the test cases and can't figure out why. Could someone prompt me in the right direction for any cases that this would fail? Thanks!
First, of all, this code is unnecessary complicated. For example, here is an equivalent but shorter solution:
def findPossibilities(stringConcat, stringList):
if not stringConcat: # if you want exact match, add `and not stringList`
return True
return any(findPossibilities(stringConcat[len(s):],
stringList[:i] + stringList[i+1:]) # assuming non-repeatable match. Otherwise, simply replace with `stringList`
for i, s in enumerate(stringList)
if stringConcat.startswith(s))
Actual answer:
Border condition: remaining part of stringConcat matches some of stringList, search is stopped:
>>> findPossibilities('aaaccbbbccc', ['aaa', 'bb', 'ccb', 'cccc'])
'aaa ccb bb'

A cleaner way to generate a list of running IDs

I just made a function to generate a list of running ids between a given range. IDs begin with an alphabet and follow with 5 numbers (e.g. A00002). The function below works, but I was wondering if there was a cleaner way to do this. Thanks!
def running_ids(start, end):
list = []
start = int(start[1:])
end = int(end[1:])
steps = end - start
def zeros(n):
zeros = 5 - len(str(n))
return zeros
while start <= end:
string = "A" + "0"*zeros(start) + str(start)
list.append(string)
start += 1
return list
print running_ids('A00001', 'A00005')
['A00001', 'A00002', 'A00003', 'A00004', 'A00005']
Use a generator. This way you can generate the numbers as needed and not store them all at once. It also maintains the state of your counter, useful if you start building large projects and you forget to add one to your index. It's a very powerful way of programming in Python:
def running_id():
n = 1
while True:
yield 'A{0:05d}'.format(n)
n += 1
C = running_id()
for n in xrange(5):
print next(C)
Giving:
A00001
A00002
A00003
A00004
A00005
You could just use simple builtin string formatting:
>>> 'A%05d'%1
'A00001'
>>> 'A{0:05d}'.format(1)
'A00001'
You can use the builtin format method
print "A" + format(1, "05d") # A00001
print "A" + format(100, "05d") # A00100
Or you can use str.zfill method like this
print "A" + str(1).zfill(5) # A00001
print "A" + str(100).zfill(5) # A00100
def running_ids(start, end):
t = start[0]
low = int(start[1:])
high = int(end[1:]) + 1
res = []
for x in range(low, high):
res.append(t + '{0:05d}'.format(x))
return res
print(running_ids('A00001', 'A00005'))

Categories

Resources