Caesar cipher - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: Homework (https://python-forum.io/forum-9.html) +--- Thread: Caesar cipher (/thread-13676.html) Pages:
1
2
|
Caesar cipher - Drone4four - Oct-26-2018 I’ve begun writing a basic Caesar cipher as part of a Udemy course by Jose Portilla that I am taking for fun. I am writing the encryption function. Jose has included some helpful pseudo code and a doc string to sort of get his students started. Here is the docstring and some pseudo code provided by the instructor: def encrypt(text,shift): ''' INPUT: text as a string and an integer for the shift value. OUTPUT: The shifted text after being run through the Caeser cipher. ''' # Create a normal plain alphabet # Create a shifted version of this alphabet # (Try slicing using the shift and then reconcatenating the two parts) # Use a for loop to go through each character in the original message. # Then figure out its index match in the shifted alphabet and replace. # It might be helpful to create an output variable to hold the new message. # Keep in mind you may want to skip punctuation with an if statement. # Return the shifted message. Use ''.join() method # if you still have it as a list. passHere is my work in progress: from collections import deque import string def encrypt(text,shift): ''' INPUT: text as a string and an integer for the shift value. OUTPUT: The shifted text after being run through the Caeser cipher. ''' alphab = string.ascii_lowercase print(alphab) alphab = deque(list(alphab)) print(alphab) alphab.rotate(shift) print(alphab) alphab = ''.join(alphab) print(alphab) passWhen I call the function with encrypt(None,3) , it produces this as output:Quote:abcdefghijklmnopqrstuvwxyzThat demonstrates that I have some of the basic functionality in place and working. The issue I’ve encountered is when I attempt to add the for loop between lines 14 and 15 so my script now looks like this: from collections import deque import string def encrypt(text,shift): ''' INPUT: text as a string and an integer for the shift value. OUTPUT: The shifted text after being run through the Caeser cipher. ''' alphab = string.ascii_lowercase print(alphab) alphab = deque(list(alphab)) print(alphab) alphab.rotate(shift) print(alphab) for index, alphab in enumerate(text): # ?????????? I"m not sure how to proceed, ugh print(index, text) alphab = ''.join(alphab) print(alphab) passWhen I call the function with encrypt('Helter Skelter! Feed the birds and deploy the vicious little piggies!',3) , that produces this:Quote:abcdefghijklmnopqrstuvwxyz The problem clearly is with my enumeration loop. What is actually happening in the loop as it appears now is this: for every item in the string (the string passed into the function - - there are 70 or so characters in total), it’s printing the full text. That’s not at all what I want. I want to apply the rotated alphabet for each character in the string. I am really struggling. Can anyone provide a little advice without completing it all for me? I’ve had some previous help with enumeration on this forum in my thread titled “Printing lines in a basic text file”. Stack Overflow’s “What does enumerate mean?” is helpful too. The course material I am working with can be found on Jose Portilla’s GitHub page. Here is the repo’s top level directory. The module I am working on is #6 called “Hacking incident”. RE: Caesar cipher - nilamo - Oct-26-2018 Ok, so I'd suggest having two different variables of the string.ascii_lowercase . One that you shift, and one that you leave unchanged. That way, while looping through the text you're changing, you can look the character up in the original, and find the new character in the shifted version.And once that's done, there's some other things we can do. But baby steps :) RE: Caesar cipher - DeaD_EyE - Oct-26-2018 Funny thing. I did not know that deque supports rotate. You can also work with translate. I'm not sure if it's in deprecation process. The funny part is, that you don't need to program your own for-loop. But it's a different way and does not answer your question. Maybe it's faster and maybe the developers remove this method. But we got also the methods for bytes back, which is very handy. RE: Caesar cipher - Drone4four - Oct-26-2018 Thank you, @nilamo, for your help so far. I’ve got a script which prints the original alphabet and an alphabet rotated by a factor of three. Here it is: from collections import deque import string def encrypt(text,shift): ''' INPUT: text as a string and an integer for the shift value. OUTPUT: The shifted text after being run through the Caesar cipher. ''' alphabet = string.ascii_lowercase # Initializing alphabet variable print(alphabet) alphabet = deque(list(alphabet)) # Turning alphabet into a list alphabet_shifted = alphabet.rotate(shift) # Assigning new variable to alphabet = ''.join(alphabet) # Re-concatenating split list print(alphabet) print(alphabet_shifted) passThat script produces the desired output: Quote:abcdefghijklmnopqrstuvwxyz Mission accomplished, right? Not quite in this case because there are some issues and quirks present that I can’t explain such as:
I figure I should prolly just nuke my script from orbit and start over. I’m think it’s the double ended que method that I am misusing and misunderstanding. Based on answers to a StackOverflow question on the ways of shifting a list in Python, here is my fresh approach to the Caesar cipher: import string def encrypt(text,shift): ''' INPUT: text as a string and an integer for the shift value. OUTPUT: The shifted text after being run through the Caeser cipher. ''' text = None alphabet = string.ascii_lowercase #Generate alphabet print(alphabet) alphabet = alphabet.split() # Split string into list alphabet_shifted = alphabet.append(alphabet.pop(shift)) # Declare shifted print(alphabet_shifted) passBased on your feedback, @nilamo, I feel that this alternate approach more clearly and concisely declares two separate alphabets - - the original and a shift of the original. As far as I can tell, the syntax, casting, and method calls above are ‘airtight’. Yet when I invoke the function encrypt(None,2) , I get this as my output:Quote:abcdefghijklmnopqrstuvwxyz Why am I getting this output? I Googled ‘pop index out of range’ and similar search terms and encountered a StackOverflow question titled, “Pop index out of range”. But I can’t see how the StackOverflow answers there address the issue with the .pop method in my script. @DeaD_EyE: I appreciate your post but it is beyond me at this point with my novice experience with Python. I'm sure it will make sense to me eventually. RE: Caesar cipher - stullis - Oct-27-2018 I did some tests and found your problem. The first thing I noticed was your output (which matched mine): Quote:abcdefghijklmnopqrstuvwxyz Since your code calls print() three times and we get three printed items, they must be all three items printed in order. So, the second item is not alphabet_shifted; instead, it's alphabet being printed on line 14. To verify that, I did this: from collections import deque import string def encrypt(text,shift): ''' INPUT: text as a string and an integer for the shift value. OUTPUT: The shifted text after being run through the Caesar cipher. ''' alphabet = string.ascii_lowercase # Initializing alphabet variable print(alphabet, 1) alphabet = deque(list(alphabet)) # Turning alphabet into a list alphabet_shifted = alphabet.rotate(shift) # Assigning new variable to alphabet = ''.join(alphabet) # Re-concatenating split list print(alphabet, 2) print(alphabet_shifted) pass encrypt("Hello again", 5)Which produced: Quote:abcdefghijklmnopqrstuvwxyz 1 The deque.rotate() method is altering the deque without providing an output. So, when you call alphabet.rotate(), you're changing alphabet. Because that method does not have a return, alphabet_shifted is instantiated as None which is why the third call to print prints "None". RE: Caesar cipher - Drone4four - Oct-28-2018 (Oct-27-2018, 12:20 AM)stullis Wrote: The deque.rotate() method is altering the deque without providing an output. So, when you call alphabet.rotate(), you're changing alphabet. I’m having an ‘ah-ha’ moment here. Like you’ve said, rotate() is not a function. It’s a method. It modifies the attribute of the object in its place. So it’s pointless to assign it as a variable. I’ve modified my script (below) by duplicating the original alphabet and the shifted alphabet. Then I proceed to manipulate the shifted alphabet in its place. Here is my code now: from collections import deque import string def encrypt(text,shift): ''' INPUT: text as a string and an integer for the shift value. OUTPUT: The shifted text after being run through the Caesar cipher. ''' original_alphabet = string.ascii_lowercase # Initializing alphabet variable print(original_alphabet, 1) original_alphabet = deque(list(original_alphabet)) # Turning alphabet into a list shifted_alphabet = original_alphabet shifted_alphabet.rotate(shift) # Rotating new shifted alphabet original_alphabet = ''.join(original_alphabet) # Re-concatenating split list (alphabet) shifted_alphabet = ''.join(shifted_alphabet) # Re-concatenating split list (shifted alphabet) print(original_alphabet, 2) print(shifted_alphabet, 3) pass encrypt(None,3)Here is the output: Quote:abcdefghijklmnopqrstuvwxyz 1 The second print statement at line 16 is still printing the shifted alphabet when I intended to print the original. @stullis I get that you’ve already addressed this point but I don’t understand what you mean when you say that since I have three printed items, they must all be printed in order. Yes, I see the three printed statements, but I am telling the Jupyter Notebook to print the original alphabet twice and the shifted alphabet once, rather than the reverse. I am still baffled. Would someone be able to clarify further? By the way, passing an integer in the three instances where the print function is invoked really helps. Thanks stullis for your contribution so far. RE: Caesar cipher - knackwurstbagel - Oct-28-2018 To figure out what is going on, why is it that line 2 and 3 of the output from your latest script are identical we need to think about what a reference is verses what a copy is or what is the difference between the '==' operator and the 'is' operator. Is a==b the same as a is b Look at this example, try to figure out what might be going on, do you really have a copy? Did you photocopy the original and thus any changes are separate? Or is it actually a clone of the original? from collections import deque import string def encrypt(text,shift): alphab = string.ascii_lowercase org = deque(list(alphab)) shifted = org shifted.rotate(shift) print(shifted is org) encrypt('test', 3) RE: Caesar cipher - stullis - Oct-28-2018 What I meant about the "printing in order" part was that the code is calling print() three times. Because this is all on a single thread, the first print item must correlate to the first print() call, the second print to the second print() call, etc. That's how I pieced together that something was up with deque.rotate() because you printed the same variable twice and got two different results. Knackwurstbagel has you covered on this one. The assignment of shifted_alphabet is a shallow copy so changing either the original or the copy will change both. RE: Caesar cipher - nilamo - Oct-28-2018 In python, variables are like boxes with labels on them. You created one deque, but stuck two different labels on that one object. So you rotated the one deque, but both variables are still pointing to the same object. The same thing happens with lists, dicts, objects, etc. The only things that don't work that way are ints/floats/strings, and a few other minor things. Because those are never changed in place, making any change to them creates a whole new object. RE: Caesar cipher - Drone4four - Nov-02-2018 Hooray! Now my original alphabet and shifted alphabet print as intended. Here is my new script: from collections import deque import string import copy def encrypt(text,shift_variance): ''' INPUT: text as a string and an integer for the shift value. OUTPUT: The shifted text after being run through the Caesar cipher. ''' original = string.ascii_lowercase # Initializing alphabet variable print(original, 1) original = deque(list(original)) # Turning the original alphabet into a list shifted = original.copy() # Assigning new variable to copy of original alphabet shifted.rotate(shift_variance) # Rotating new shifted alphabet original = ''.join(original) # Re-concatenating split list (alphabet) shifted = ''.join(shifted) # Re-concatenating split list (shifted alphabet) print(original, 2) print(shifted, 3) passAt line thirteen I invoke the copy function from the copy module. I learned about this technique by Googling around for ‘python copy reference’ and similar search terms as introduced by @knackwurstbagel and @nilamo. In particular, the explanation found over in a Stackoverflow question titled “How to clone or copy a list?“ helped tremendously. Here is my new output: Quote:abcdefghijklmnopqrstuvwxyz 1 Now we can return to writing a for loop to loop through each character in the message entered by the user. As a reminder, here is the original doc string and pseudo code provided by the instructor for the encrypt function: def encrypt(text,shift): ''' INPUT: text as a string and an integer for the shift value. OUTPUT: The shifted text after being run through the Caeser cipher. ''' # Create a normal plain alphabet # Create a shifted version of this alphabet # (Try slicing using the shift and then reconcatenating the two parts) # Use a for loop to go through each character in the original message. # Then figure out its index match in the shifted alphabet and replace. # It might be helpful to create an output variable to hold the new message. # Keep in mind you may want to skip punctuation with an if statement. # Return the shifted message. Use ''.join() method # if you still have it as a list. passI can check off a number of these lines as already implemented. I’ve created a plain alphabet and created a shifted alphabet. I’ve returned a shifted alphabet and joined them together. Now I am going to tackle the for loop. Here is my script: from collections import deque import string import copy def encrypt(text,shift_variance): ''' INPUT: text as a string and an integer for the shift value. OUTPUT: The shifted text after being run through the Caesar cipher. ''' original = string.ascii_lowercase # Initializing alphabet variable original = deque(list(original)) # Turning the original alphabet into a list shifted = original.copy() # Assigning new variable to copy of original alphabet shifted.rotate(shift_variance) # Rotating new shifted alphabet # BEGIN for loop: text = list(text) # Convert text to string print(text) # Confirmation of conversion operation scrambled_text =[] # Initializing output variable for index in text: for variable, char in enumerate(shifted): variable[char] = scrambled_text passWhen I call the encryption function using encrypt("Hello World",3) , I get this output:Quote:['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'] What is going wrong in my script? shifted is the list of characters in the shifted alphabet, so it’s iterable. I Googled ‘TypeError: 'int' object does not support item assignment’ which turned up a Stackoverflow question with that TypeError in the title but the explanation completely escapes me. I'm trying to use a for loop to go through each character in the original message and then assign a new letter from the shifted alphabet to each index match in the input text. Or in other words, what I am stuck on the most here is how to come up with the algorithm (a for loop or combination of two for loops) to 'replace' one character in the input text with a letter in the shifted alphabet. When "replacing" is used by the instructor in the pseudo code, I am not sure how to translate that into Python code. This is my biggest hurdle. Can someone here provide some guidance and clarity? Other resources I’ve used include Ned Batchelder’s talk on YouTube titled “Loop like a native: while, for, iterators, generators”. I’ve had some previous help with enumeration on this forum in my thread titled “Printing lines in a basic text file” along with Stackoverflow’s “What does enumerate mean?”. |