Python Forum

Full Version: Padlock of alphabetical strings
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
Hi guys,

This actually is not my homework assignment. It's someone else's that I thought to try and see if I could get it running.
I've coded it to the best of my abilities however, I know that there is a more pythonic way of coding it.

When executing the code, I noticed that according to the indentation of the print statement it either runs continuously or doesn't run at all.
The assignment is:
1. Given two strings (strings s and f): one of English lowercase letters and the other of favorite English lower case letters.
2. perform an operation to change the first letter in string to the previous or next letter alphabetically then loop back until it is == to that of string f:
for example if s = j and f = a loop would be ran until 'j' eventually becomes 'a'.
3. The output is text format Case #x: y operations. x representing incremented case number and y representing the total amount of operations undergone to transform the letter.

Here's what I have:
import random
import string 

def randomLowerAlpha(): # generates random letters of the alphabet in the range of 6 
    return str("".join(random.choice(string.ascii_lowercase) for _ in range(6)))

def match_string(str_1, str_2):
    index = 0
    case = 0
    op_count = 0
    for char in str_1:
        curr_pos = index
        valS = ord(str_1[curr_pos])
        valF = ord(str_2[curr_pos])
        while valS > valF:
            op_count += 1
            if valS > valF:
                for i in range(valS, valF, -1):
                    prev_letter = i
                    new_str = str_1.replace(str_1[curr_pos], chr(prev_letter))
                    if valS == valF:
                        break
            else:
                while valS < valF:
                    op_count += 2
                if valS < valF:
                    for i in range(valS, valF, +1):
                        next_letter = i
                    new_str = str_1.replace(str_1[curr_pos], chr(next_letter))
                    if valS == valF:
                        break
            case += 1
            print(f"Case #{case}: {op_count} operations.")
            break

s = randomLowerAlpha()
f = 'aeiouy'
match_string(s,f)
Thank you in advance.
Cheers,
there is an increment of case and a break statement after the print, this is controlling flow, not the print statement.

if you indent the block containing case and break statement one level it will, of course, change program flow.
the print statement is only displaying the value of case and op_count, has nothing to do with program control.
Ok, I did a little switching around.

Now more or less, I'm getting the desired output.
But the op_count incrementation is some how off.
The op_count is the number of steps backwards or forward required to reach the n(value)
the distance from v to e is 17+1 steps and it's giving me 27.
This is what I did: (this is just the first half, else hasn't come in yet)
import random
import string

def match_string(str_1, str_2):
    case = 0
    op_count = 0
    for index, value in enumerate(str_1):
        cur_position, cur_value = index, value
        valS = ord(str_1[cur_position])
        valF = ord(str_2[cur_position])
        if valS > valF:
            case += 1
            for i in range(valS, valF-1, -1):
                prev_letter = i
                op_count += 1
            new_str = str_1.replace(chr(valS), chr(prev_letter))
            cur_position = cur_position + 1
            if valS == valF:
                break
            print(f"Case #{case}: {op_count} Operations.")

s = 'ivhfmh'
f = 'aeiouy'
match_string(s,f)
This is the out put:
Output:
Case #1: 9 Operations. Case #2: 27 Operations.
What am I doing wrong?
(Jan-13-2022, 10:47 AM)Larz60+ Wrote: [ -> ]there is an increment of case and a break statement after the print, this is controlling flow, not the print statement.

if you indent the block containing case and break statement one level it will, of course, change program flow.
the print statement is only displaying the value of case and op_count, has nothing to do with program control.
You don't reset op_count. You probably don't want to reset op_code because it keeps track of the total number of "+" steps. Stepping from "a" to "i" is 8 "+" steps. Stepping "e" to "v" is 17 "+" steps. 17 + 8 == 25. Oops! Your code says there are 9 and 27 steps. Where are the extra steps coming from?

The extra step is here:
for i in range(valS, valF-1, -1):
                op_count += 1
You are counting fence posts when you should be counting sections. Take the case of stepping "a" to "i". The steps would be:
a->b->c->d->e->f->g->h->i
If you count the letters you get 9, but you should be counting the steps, not the letters. Each "->" is a step. Count them up and you get 8 steps. Your loop should go something like this:
for i in range(valS, valF, -1):
                op_count += 1
This gives the correct number of steps, but why are you looping at all? You already know the number of steps = valF - valS. You don't need to write a loop to increment a counter if you already know how many times the loop will execute. Would you do math like this?
# Add 8 + 17
total = 0
for _ in range(8):
    total += 1
for _ in range(17):
    total += 1
I hope not! But this is what you are doing in your code. You have too many loops in your code. There should only be one. You also have too many "if" statements. You only need one (or maybe none).
K...switched things up again...
Close but no cigar...cheated a bit because I couldn't get my "op_count" variable to work so I just subtracted the numerical values from each other and got the steps and assigned the results to my variable. For some reason the calc is stil off.
import random
import string

def randomLowerAlpha():
    return str("".join(random.choice(string.ascii_lowercase) for _ in range(6)))

def match_string(str_1, str_2):
    case = 0
    op_count = 0
    for i, j in zip(str_1, str_2):
        case += 1
        while i > j:
            for val in range(ord(i), ord(j)-1, -1):
                 prev_letter = val
                 op_count = ord(i) - ord(j)
            new_str = str_1.replace(str_1[0], chr(prev_letter))
            if prev_letter == j:
                break
            print(f"Case #{case}: {op_count} Operations.")
            break
        else:
            while i < j:
                for val in range(ord(i), ord(j)-1, +1):
                     prev_letter = val
                     op_count = abs(ord(i) - ord(j)+1)
                new_str = str_1.replace(str_1[0], chr(prev_letter))
                if prev_letter == j:
                    break
                print(f"Case #{case}: {op_count} Operations.")
                break

# s = randomLowerAlpha()
f = 'aeiouy'
b = 'ivhfmh'
match_string(b,f)
Here's the output:
Output:
Case #1: 8 Operations. Case #2: 17 Operations. Case #3: 17 Operations. Case #4: 8 Operations. Case #5: 7 Operations. Case #6: 16 Operations.
Case 3 is returning 17 while it's supposed to be 1...go figure
I think you are taking this too literally:
Quote:2. perform an operation to change the first letter in string to the previous or next letter alphabetically then loop back until it is == to that of string f:
for example if s = j and f = a loop would be ran until 'j' eventually becomes 'a'.
Note that the return value is not the string, because the modified string will be the same as the "favorite" string. Your function's purpose is to compute the number of "operations" required to convert the input string to the favorite string.

You could follow the instructions very literally and actually "preform an operation" on each letter until the input string matches the favorite string.
def string_stepper(string, pattern):
    """Calculate number of steps required to move letters in string to the corresponding
    letter in pattern.
    """
    plus = minus = 0
    for s, p in zip(string, pattern):
        while s > p:
            s = chr(ord(s)-1)  # The "Operation"
            minus += 1
        while s < p:
            s = chr(ord(s)+1)
            plus += 1
    return plus + minus, plus, minus

total, plus, minus = string_stepper('ivhfmh', 'aeiouy')
print(f"Total {total}, +{plus}, -{minus}")
This does work, but why would anyone solve the problem this way? It is just as goofy as my earlier "doing addition using for loops" example. You know the ordinal value of the letters. The ordinal value is an integer. You can do math with integers. Do you need a loop to do math?
First of all, understand that I am learning something that is completely new to me.
The logic that I apply is how it's translated from my head to the code. I don't fully understand
the structure of things yet. At least I'm trying. Even me as a beginner I've seen some people who are a step or two behind me ask what you might call a silly question on the basics of python. Yet because I understand what they're going through I support them with the code explaining each step for ease of reference but that's who I am.

Now, the question is not:
Quote: but why would anyone solve the problem this way?
but what is the most efficient way of doing it (structure wise)?
Once I understand this then the goofy coding will stop.

Thx for the code by the way...I'll dissect it in order to understand certain things.
Cheers,
(Jan-13-2022, 10:48 PM)deanhystad Wrote: [ -> ]I think you are taking this too literally:
Quote:2. perform an operation to change the first letter in string to the previous or next letter alphabetically then loop back until it is == to that of string f:
for example if s = j and f = a loop would be ran until 'j' eventually becomes 'a'.
Note that the return value is not the string, because the modified string will be the same as the "favorite" string. Your function's purpose is to compute the number of "operations" required to convert the input string to the favorite string.

You could follow the instructions very literally and actually "preform an operation" on each letter until the input string matches the favorite string.
def string_stepper(string, pattern):
    """Calculate number of steps required to move letters in string to the corresponding
    letter in pattern.
    """
    plus = minus = 0
    for s, p in zip(string, pattern):
        while s > p:
            s = chr(ord(s)-1)  # The "Operation"
            minus += 1
        while s < p:
            s = chr(ord(s)+1)
            plus += 1
    return plus + minus, plus, minus

total, plus, minus = string_stepper('ivhfmh', 'aeiouy')
print(f"Total {total}, +{plus}, -{minus}")
This does work, but why would anyone solve the problem this way? It is just as goofy as my earlier "doing addition using for loops" example. You know the ordinal value of the letters. The ordinal value is an integer. You can do math with integers. Do you need a loop to do math?
Programming has little to do with writing code and much to do with problem solving. If you had to solve this problem with pencil and paper you might start out by writing down the alphabet to make it easier to count.
abcdefghijklmnopqrstuvwxyz
|12345678            |  a->i = 8 steps
    |12345678901234567  e->v = 17 steps
This works fine for a few short strings, but if you had to do 100 long strings you would quickly find it is much faster to write down the ordinal values of all the letters and do the math.
a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z
1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
a->i = i - a = 9 - 1 = 8 steps
e->v = v - a = 22 - 5 = 17 steps
Converting to Python
plus = minus = 0
for s, p in zip(string, pattern):
    steps = ord(p) - ord(s)
    if steps < 0:
        minus -= steps
    else
        plus += steps
print("Total steps", plus + minus, "+", plus, "-", minus)
Thx for this...I actually understood the logic of the other code you provided when I went through it yesterday. It was as simple as 1,2,3.

On the other hand, I was thinking that the assignment was maybe designed the way it was to make one understand what happens with loops/iteration. That's mainly the reason why I was so focused on looping/iterating.

Once again, thx for the assist.

Cheers,

(Jan-14-2022, 06:56 PM)deanhystad Wrote: [ -> ]Programming has little to do with writing code and much to do with problem solving. If you had to solve this problem with pencil and paper you might start out by writing down the alphabet to make it easier to count.
abcdefghijklmnopqrstuvwxyz
|12345678            |  a->i = 8 steps
    |12345678901234567  e->v = 17 steps
This works fine for a few short strings, but if you had to do 100 long strings you would quickly find it is much faster to write down the ordinal values of all the letters and do the math.
a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z
1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
a->i = i - a = 9 - 1 = 8 steps
e->v = v - a = 22 - 5 = 17 steps
Converting to Python
plus = minus = 0
for s, p in zip(string, pattern):
    steps = ord(p) - ord(s)
    if steps < 0:
        minus -= steps
    else
        plus += steps
print("Total steps", plus + minus, "+", plus, "-", minus)
Hopefully the purpose of this assignment was to show you how easy the solution to a problem can be when you actually spend some time thinking about how to solve the problem instead of blindly following instructions.
Pages: 1 2