newB struggling with Backus Naur at Udacity Comp. Sci. 101 - python

I am finishing up the Intro to Computer Science 101 course at Udacity and am looking for some help addressing one of the final quiz problems. The following code returned a "pass" when submitted but I feel like I am not getting at the heart of the challenge in this quiz. Any help or suggestions about how to approach and think about the problem would be appreciated.
The problem:
"""
Define a Python procedure, in_language(<string>),
that takes as input a string and returns True
if the input string is in the language described by the BNF grammar below
(starting from S) and returns False otherwise.
BNF grammar description:
S => 0 S 1
S => 1 S 0
S => 0
"""
# Tests. These all should print True if your procedure is defined correctly
print in_language("00011") == True
print in_language("0") == True
print in_language("01") == False
print in_language("011") == False
print in_language("01002") == False
Here's my code so far:
def in_language(bnf):
if len(bnf) % 2 == 0:
return False
if any(i in '23456789' for i in bnf) == True:
return False
if bnf[len(bnf)/2] != '0':
return False
else:
return True
This code will return True for submissions not of the given Backus-Naur Form:
S => 0 S 1
S => 1 S 0
S => 0
such as '11111011111'
print in_language('11111011111') == False
I am still wrapping my head around recursion, but it seems like there is a way to address this problem recursively? Either that or my next step would have been to check the first and last characters of the string to see if they were exclusively a zero and a one (not both), then remove them and continue pruning the string until I got to the base case, or, "middle" zero. I was surprised that the code passed the quiz at this point.
Of note, my thinking about the code:
if len(bnf) % 2 == 0:
The first if conditional occurred to me because given the B-N form, any iteration will result in an odd number of numbers, so string length divisibility by 2 indicates something not of that form.
if any(i in '23456789' for i in bnf) == True:
The second "if" was also a simply consideration because the problem is only looking for strings constructed of ones and zeros (I suppose I could have included the alphabet as well, or simply written if any(i not in '01' for i in bnf) .
if bnf[len(bnf)/2] != '0':
The third "if" is similarly looking for a qualifying feature of the given B-N form - no matter what the expression according to the given syntax, there will be a zero in the middle - and making use of Pythons floor division as well as the index start at zero.
Any thoughts or suggestions of alternative solutions would be greatly appreciated, thanks!
As I am new to StackOverflow, I did research this question before posting. Any posting style considerations (too verbose?) or concerns would also be helpful :)
Okay,
I took duskwoof's suggestion and came up with this:
def in_language(bnf):
# corresponding to: S => 0 S 1
if bnf[0] == '0' and bnf[-1] == '1':
return in_language(bnf[1:-1])
# corresponding to: S => 0 S 1
if bnf[0] == '1' and bnf[-1] == '0':
return in_language(bnf[1:-1])
# corresponding to: S => 0
if bnf == '0':
return True
return False
and it works for cases which follow the form, but python barfs when I submit cases which do not... and I still feel like there is something I am missing about recursion and parsing the strings for Backus-Naur Forms. How should I think about handling the cases which don't follow the form? Thank you for the help. I'll keep wrestling with this.
This seems to work better - passes all the test cases:
def in_language(bnf):
if len(bnf) > 2:
# corresponding to: S => 0 S 1
if bnf[0] == '0' and bnf[-1] == '1':
return in_language(bnf[1:-1])
# corresponding to: S => 0 S 1
if bnf[0] == '1' and bnf[-1] == '0':
return in_language(bnf[1:-1])
# corresponding to: S => 0
if bnf == '0':
return True
return False
but again, I'm totally a newB #programming, so any advice or input would be very helpful... I still don't feel like I have a very general solution; just something specific to this particular BNF grammar.

I am still wrapping my head around recursion, but it seems like there is a way to address this problem recursively?
This is precisely how you are expected to approach this problem. Don't try to overthink the problem by analyzing what properties the strings in this language will have (e.g, length modulo 2, what characters it will contain, etc)! While this could potentially work for this specific language, it won't work in general; some languages will simply be too complicated to write an iterative solution like you're describing.
Your solution should be a direct translation of the description of the language -- you shouldn't have to think too much about what the rules mean! -- and should use recursion for rules which have S on the right side. It should be written in the form:
def in_language(bnf):
if ...: # corresponding to: S => 0 S 1
return True
if ...: # corresponding to: S => 1 S 0
return True
if ...: # corresponding to: S => 0
return True
return False
(The solution you currently have is a "false solution" -- it will pass the tests given in the problem, but will fail on certain other inputs. For example, the string 000 is not in this language, but your function will say it is.)

Related

Brute Force Created In Python | Numerical Only | Up to 4 Digits

Thank you very much for taking the time to read this. I am a tadpole when it comes to write python and right now I just written the framework to create a brute force algo to sort up to 3 digits worth of numbers. Am I in the right direction?
I am assuming the web app actually reveal the number of digits it is send to your email. I first learn about this from TryHackMe.
Now it can randomly create up to 4 digit numbers as seen in BookFace and the above can crack the code as seen using their example. My question is am I doing it in the right way? Because I seen other people's sample of bruteforce and they use a lot of function. Am I being too long winded?
import random
digits = 4 #reveal the number of digits
i=0
z=0
x="" #init password to nothing
#generate random password up to 4 digits
while i < digits:
z = random.randint(0,9)
print(z)
x = str(x) + str(z)
i+=1
y = 1
print("Code Random Generated: " + x)
if digits == 1:
while y!=int(x):
print(y)
y+=1
elif digits == 2:
while y!=int(x):
if len(str(y)) == 1:
print("0" + format(y))
elif len(str(y)) == 2:
print(y)
y+=1
elif digits == 3:
while y!=int(x):
if len(str(y)) ==1:
print("00" + format(y))
elif len(str(y)) == 2:
print("0" + format(y))
elif len(str(y)) == 3:
print(y)
y+=1
elif digits == 4:
while y!=int(x):
if len(str(y)) ==1:
print("000" + format(y))
elif len(str(y)) == 2:
print("00" + format(y))
elif len(str(y)) == 3:
print("0" + format(y))
elif len(str(y)) == 4:
print(y)
y+=1
if y !=0:
print("Reset Code Revealed: " + format(y))
You are sort of asking for a code review, not for a problem's solution. This is what Code Review Stack Exchange is for.
Anyway, let's look at my own solution to the problem :
"""
An humble try at cracking simple passwords.
"""
import itertools
import random
from typing import Callable, Iterable, Optional
def generate_random_numerical_password(ndigits: int) -> str:
return "".join(str(random.randint(0, 9)) for _ in range(ndigits))
def guesses_generator(ndigits: int) -> Iterable[str]:
yield from ("".join(digits) for digits in itertools.product("0123456789", repeat=ndigits))
PasswordOracle = Callable[[str], bool]
def cracker(ndigits: int, oracle: PasswordOracle) -> Optional[str]:
for guess in guesses_generator(ndigits):
if oracle(guess):
return guess
else:
return None
if __name__ == "__main__":
NDIGITS = 4 # difficulty
print(f"the difficulty (number of digits) is set to {NDIGITS}")
password = generate_random_numerical_password(NDIGITS)
print(f"the password to crack is {password!r}")
password_oracle = lambda guess: guess == password # do not use `is`, see Python's string interning
if match := cracker(NDIGITS, password_oracle):
print("Cracked!")
else:
print("Error, not found")
Some differences :
The different parts of the program are clearly delimited using functions (password creation and cracking).
The guess generation is generalized : if the number of digits become 5, you will have to write another (tedious and error-prone) elif.
The "flow" of the program is made very clear in the "main" part : generate a password and the corresponding oracle, call the cracker and check if a result was found. There is very few lines to read, and using descriptive names helped too.
The names come from the domain ("cracking") : "guess", "difficulty", "generator" instead of abstract ones like "x" and "y".
Some language knowledge is used : standard types operations (str-joining instead of concatenation), generators (yield from), libraries (itertools.product), syntax (walrus oparetor if match := cracker(...), ...
There is more documentation : comments, docstring at the top of the module, type annotations, ... all of these helping to understand how the program works.
My question is am I doing it in the right way? Because I seen other people's sample of bruteforce and they use a lot of function. Am I being too long winded?
I did not do use functions on purpose to do like the others people, but because I see things in a different way than you, which I will try to explain.
In the end, both your solution and mine solve the initial problem "crack a four-digit password". In this way, they are not much different. But there could be other ways to consider :
Will the problem change ? Could the password be 5 digits, or contain alphabetic characters, or special characters ? In such cases, how long will it take to adapt the script ? This is the malleability of the code.
How clear is the code ? If I have a bug to fix, how long will it take me to find where it comes from ? What if it is someone new to Python ? Or someone that knows Python well but never saw the code ? This is the maintainability of the code.
How easy is it to test the code ? What can be tested ? This is teastability.
How fast is the code ? How much memory does it uses ? This is performance ?
Can the program crash ? What if the program is given bad inputs ? Could it cause an infinite loop ? This is program-safety.
...
Depending on how your objectives about malleability, maintainability, testability, performance, safety, ... (other qualities of a program), but also depending on the context. who is writing the code ? Who will read it later ? How much experienced they are ? How much time do they have to finish writing it ? Will it be run only once then thrown away or will it be deployed on millions of devices ?
All of that affects how you write it. If I was in your shoes (beginner to Python, writing a run-once-then-forget script) I would have done the same as you. The difference is the context and the objectives. I wrote my code as an example to show you the difference. But neither is good nor bad.
You are not bad at running just because you are slower than an Olympic athlete. You can only be bad relative to a context and objectives. In Physical Education class, you are graded on your running speed according to the average for your age and the progress you made.
Seeing things in their perspective is a very useful skill, not just for programming.
Whan you compare your code to other's, yours seem less "clever", less "clean", less "elegant". But you are comparing apples to oranges. You are not the others. If your solution was accepted (correct and fast enough) that's a good start at your level.
My years of professionnal experience working with several other people on tens-of-thousand-lines codebases that have to be maintained for 20 years are a different set of contexts and objectives than you learning the language in fun ways (TryHackMe). Neither is objectively bad, both are subjectively good.
TL;DR : your code is fine for a beginner, you still have lots to learn if you want to, and keep having fun !

Why is my function checking for English apps returning True for non-English apps?

I'm currently doing an exercise in a guided project on dataquest.io in Jupyter Notebook. My goal is to write a function that checks if the characters in a given string each have an ASCII number of over 127, and if the string has more than three of these, to return False.
Github link to entire code - problematic function located in Cell 20
I've tried Restart and Run All in the kernel of my Jupyter Notebook which has helped debug in the past but not this time. I've also looked at the solution for this exercise and my code is literally the same. Yet, my code is returning True for an obviously non-English app name.
def is_english(string):
non_ascii = 0
for c in string:
char = ord(c)
if char > 127:
non_ascii += 1
if non_ascii > 3:
return False
else:
return True
print(is_english('爱奇艺PPS -《欢乐颂2》电视剧热播'))
print(is_english('Instachat 😜'))
Output:
True
True
Expected results were:
False
True
I theorize maybe something in the larger project is an issue, as my code matches the solution code. I just can't get a grasp on what is happening. Thank you from a beginner!
The issue is with your if/else: both paths return, so you are always returning after checking the first character.
You could fix this by returning True outside the for loop, so the loop goes over every character before returning True:
def is_english(string):
non_ascii = 0
for c in string:
char = ord(c)
if char > 127:
non_ascii += 1
if non_ascii > 3:
return False
return True
print(is_english('爱奇艺PPS -《欢乐颂2》电视剧热播'))
print(is_english('Instachat 😜'))
The linked solution is correct, and matches yours in all but indentation! If you look at the solution indentations, you can see their if/else is done after the loop finishes.

Python - PseudoCode for functions and operands

I am quite new to the PseudoCode concept... I would like to get an idea of how functions and operands like modulus, floor division and the likes would be written in PseudoCode. Writing the PseudoCode for this code might actually help me understand better...
user_response=input("Input a number: ")
our_input=float(user_response)
def string (our_input):
if (our_input % 15) == 0 :
return ("fizzbuzz")
elif (our_input % 3) == 0 :
return ("fizz")
elif (our_input % 5) == 0 :
return ("buzz")
else :
return ("null")
print(string(our_input))
Your code really isn't that hard to understand assuming the knowledge of % as the modulus operation. Floor division can really just be written as a regular division. Floor the result, if really necessary.
If you don't know how to express them, then explicitly write modulus(x, y) or floor(z).
Pseudocode is meant to be readable, not complicated.
Pseudocode could even be just words, and not "code". The pseudo part of it comes from the logical expression of operations
The basic idea of PseudoCode is either to
a) Make complicated code understandable, or
b) Express an idea that you are going to code/haven't yet figured out how to code.
For example, if I am going to make a tool that needs to read information in from a database, parse it into fields, get just the info that the user requests, then format the information and print it to the screen, my first draft of the code would be simple PseudoCode as so:
# Header information
# Get user input
# Connect to Database
# Read in values from database
# Gather useful information
# Format information
# Print information
This gives me a basic structure for my program, that way I don't get lost as I'm making it. Also, if someone else is working with me, we can divvy up the work (He works on the code to connect to the database, and I work on the code to get the user input.)
As the program progresses, I would be replacing PseudoCode with real, working code.
# Header information
user_input_row = int(input("Which row (1-10)? "))
user_input_column = input("Which column (A, B, C)? "))
dbase = dbconn("My_Database")
row_of_interest = dbase.getrow(user_input_row)
# Gather useful information
# Format information
# Print information
At any point I might realize there are other things to do in the code, and if I don't want to stop what I'm working on, I add them in to remind myself to come back and code them later.
# Header information #Don't forget to import the database dbconn class
user_input_row = int(input("Which row (1-10)? "))
#Protect against non-integer inputs so that the program doesn't fail
user_input_column = input("Which column (A, B, C)? "))
#Make sure the user gives a valid column before connecting to the database
dbase = dbconn("My_Database")
#Verify that we have a connection to the database and that the database is populated
row_of_interest = dbase.getrow(user_input_row)
# Separate the row by columns -- use .split()
# >> User only wants user_input_column
# Gather useful information
# Format information
# >> Make the table look like this:
# C C1 C2 < User's choice
# _________|________|_______
# Title | Field | Group
# Print information
After you're done coding, the old PseudoCode can even serve to be good comments to your program so that another person will know right away what the different parts of your program are doing.
PseudoCode also works really well when asking a question when you don't know how to code something but you know what you want, for example if you had a question about how to make a certain kind of loop in your program:
my_list = [0,1,2,3,4,5]
for i in range(len(my_list)) but just when i is even:
print (my_list[i]) #How do I get it to print out when i is even?
The PseudoCode helps the reader know what you're trying to do and they can help you easier.
In your case, useful PseudoCode for things like explaining your way through code might look like:
user_response=input("Input a number: ") # Get a number from user as a string
our_input=float(user_response) # Change that string into a float
def string (our_input):
if (our_input % 15) == 0 : # If our input is divisible by 15
return ("fizzbuzz")
elif (our_input % 3) == 0 : # If our input is divisible by 3 but not 15
return ("fizz")
elif (our_input % 5) == 0 : # If our input is divisible by 5 but not 15
return ("buzz")
else : # If our input is not divisible by 3, 5 or 15
return ("null")
print(string(our_input)) # Print out response
THANKS GUYS FOR ALL OF YOUR INPUT, FROM ALL I READ, I CAME UP WITH THIS, IF THERE ARE ANY AREAS U THINK NEEDS TO BE ADJUSTED PLEASE LET ME KNOW.
THANKS AGAIN
THE FUNCTION USING PYTHON
user_response=input("Input a number: ")
our_input=float(user_response)
def string (our_input):
if (our_input % 15) == 0 :
return ("fizzbuzz")
elif (our_input % 3) == 0 :
return ("fizz")
elif (our_input % 5) == 0 :
return ("buzz")
print(string(our_input))
<<------------------------------->>
IT'S PSEUDOCODE
Request an input from the user
Ensure that the input is a number
If the input is divisible by 15
send "fizzbuzz" to the main
program
But if the input is divisible by 3 and not 15
send "fizz" to the main program
But if the input is divisible by 5 and not 15 or 3
send "buzz" to the main program
Display the result.

python boolean quest. for if statement

I tried this... but it doesn't work
question = input("do you want the program to start? Type Y/y to start: ")
y = TRUE
Y = TRUE
if(question == TRUE):
run statements
else:
what am i doing wrong? this doesn't work.
To answer your specific question, it is not working because of these issues:
TRUE is not a defined variable in python. True is.
question == TRUE won't work. See 1.
run statements isn't real code.
You need to put something in your else: block.
EDIT:
question can never become True in this code. – #adsmith
NOTE:
Just trying to be comprehensive with my coverage.
The boolean is True in Python, not true or TRUE. In any case, this doesn't do what you expect it to. This is what I'd do.
question = input("...")
if question.lower() == 'y': # or `question in ('y','Y'):` or `question.upper() == "Y":` or `question.casefold() == 'y':` or................
do_things
else:
handle_it
What you had written assigns the variables y and Y to some (undefined) variable TRUE. This will trigger a NameError since there is no such variable TRUE. If you had done:
y = True
Y = True
It still wouldn't have done what you wanted, since your input (fed into the variable question) is a string and those are variables. You could have done that with if globals()[question] but that's really bad practice, and COMPLETELY unnecessary in this situation.
As a side note -- there's never a reason to type == True. if foo will evaluate to True or False, which will fulfill the conditional on its own. It just does a needless compare :)
I think you probably want to use code something along these lines:
answer = raw_input("Do you want the program to start? Type Y/y to start: ")
if answer[0].lower() != "y": # first character not a "Y" or "y"?
exit()
rest of program...
1) You have five (5) spaces indenting your if clause. Should follow Generally Accepted Python Practices (GAPP) ;) (Yes, I just made this up. It may or may not become a thing :p) you should use four (4) spaces.
2) Try adding pass after else:
else:
pass
3) In Python, case matters. As such, boolean testing must be True or False (or 1/0 :p)
4) Don't you mean Y/N? Not Y/y?

Serial Key Generation and Validation

I'm toying around with writing creating a serial code generator/validator, but I can't seem to get how to do a proper check.
Here's my generator code:
# Serial generator
# Create sequences from which random.choice can choose
Sequence_A = 'ABCDEF'
Sequence_B = 'UVWQYZ'
Sequence_C = 'NOPQRS'
Sequence_D = 'MARTIN'
import random
# Generate a series of random numbers and Letters to later concatenate into a pass code
First = str(random.randint(1,5))
Second = str(random.choice(Sequence_A))
Third = str(random.randint(6,9))
Fourth = str(random.choice(Sequence_B))
Fifth = str(random.randint(0,2))
Sixth = str(random.choice(Sequence_C))
Seventh = str(random.randint(7,8))
Eighth = str(random.choice(Sequence_D))
Ninth = str(random.randint(3,5))
serial = First+Second+Third+Fourth+Fifth+Sixth+Seventh+Eighth+Ninth
print serial
I'd like to make a universal check so that my validation code will accept any key generated by this.
My intuition was to create checks like this:
serial_check = raw_input("Please enter your serial code: ")
# create a control object for while loop
control = True
# Break up user input into list that can be analyzed individually
serial_list = list(serial_check)
while control:
if serial_list[0] == range(1,5):
pass
elif serial_list[0] != range(1,5):
control = False
if serial_list[1] == random.choice('ABCDEF'):
pass
elif serial_list[1] != random.choice('ABCDEF'):
control = False
# and so on until the final, where, if valid, I would print that the key is valid.
if control == False:
print "Invalid Serial Code"
I'm well aware that the second type of check won't work at all, but it's a place holder because I've got no idea how to check that.
But I thought the method for checking numbers would work, but it doesn't either.
The expression `range(1, 5)' creates a list of numbers from 1 to 4. So in your first test, you're asking whether the first character in your serial number is equal to that list:
"1" == [1, 2, 3, 4]
Probably not...
What you probably want to know is whether a digit is in the range (i.e. from 1 to 5, I assume, not 1 to 4).
Your other hurdle is that the first character of the serial is a string, not an integer, so you would want to take the int() of the first character. But that will raise an exception if it's not a digit. So you must first test to make sure it's a digit:
if serial_list[0].isdigit() and int(serial_list[0]) in range(1, 6):
Don't worry, if it's not a digit, Python won't even try to evaluate the part after and. This is called short-circuiting.
However, I would not recommend doing it this way. Instead, simply check to make sure it is at least "1" and no more than "5", like this:
if "1" <= serial_list <= "5":
You can do the same thing with each of your tests, varying only what you're checking.
Also, you don't need to convert the serial number to a list. serial_check is a string and accessing strings by index is perfectly acceptable.
And finally, there's this pattern going on in your code:
if thing == other:
pass
elif thing != other:
(do something)
First, because the conditions you are testing are logical opposites, you don't need elif thing != other -- you can just say else, which means "whatever wasn't matched by any if condition."
if thing == other:
pass
else:
(do something)
But if you're just going to pass when the condition is met, why not just test the opposite condition to begin with? You clearly know how to write it 'cause you were putting it in the elif. Put it right in the if instead!
if thing != other:
(do something)
Yes, each of your if statements can easily be cut in half. In the example I gave you for checking the character range, probably the easiest way to do it is using not:
if not ("1" <= serial_list <= "5"):
Regarding your python, I'm guessing that when your wrote this:
if serial_list[0] == range(1,5):
You probably meant this:
if 1 <= serial_list[0] <= 5:
And when you wrote this:
if serial_list[1] == random.choice('ABCDEF'):
You probably meant this:
if serial_list[1] in 'ABCDEF':
There are various other problems with your code, but I'm sure you'll improve it as you learn python.
At a higher level, you seem to be trying to build something like a software activation code generator/validator. You should know that just generating a string of pseudo-random characters and later checking that each is in range is an extremely weak form of validation. If you want to prevent forgeries, I would suggest learning about HMAC (if you're validating on a secure server) or public key cryptography (if you're validating on a user's computer) and incorporating that into your design. There are libraries available for python that can handle either approach.

Categories

Resources