CS30 - Spring 2015 - Class 12
Example code in this lecture
shows one way of implementing the CFG we saw in the slides
- terminals are represented as strings
- each nonterminal is represented as a function, e.g. noun, verb, verb_phrase, etc.
- they return one of two things, representing the right hand side of the rule:
- a string
- a list of functions (could also be a list of functions and strings, though we don't utilize that option)
- if there are multiple options (i.e through |) then a random choice is made
- noun, verb, article, adjective and adverb all generate only terminals
- they randomly select between possible words (strings) using random.choice
- they each return a string
- pre_noun_phrase, noun_phrase, verb_phrase and sentence all generate more nonterminals (though they don't necessarily have to)
- they each return a list of functions, representing a rule that generates a nonterminal on the right hand side
the process function in
takes in a cfg rule (either a string or a list of functions) and randomly generates a string in that language
- the function is recursive!
- this is not too surprising since CFG have a recursive feeling to them
- the base case is if we're dealing with a terminal (i.e a string)
- the recursive case is if we have a list of nonterminals (i.e. functions)
- for each item (function) in this list:
- call the function "item()"
- and then call process on whatever it give in return
- this will either be a string (in the case of a rule that generates a terminal)
- or another list of functions
- the results are then appended together into one string
- we can call the process function by getting it started with [sentence], i.e.
'the bagel eats intentionally'
- for this to happen, the recursion would be something like:
- top call: process([sentence])
- this would then make two recursive calls to return: process(noun_phrase()) + " " + process(verb_phrase())
- the call to noun_phrase returns [article, pre_noun_phrase]
- which then is passed to process
- article gives us 'the'
- and the pre_noun_phrase pics just [noun]
- the noun "bagel" is chosen
printing out lots of random derivations from the grammar
- if we put this call in a loop, we can get lots of random strings:
>>> for i in range(10):
... print process([sentence])
a milk eats
a bagel sleeps
a idea sleeps furiously
a green idea eats soothingly
a green cow sleeps intentionally
the colorless smelly cow sleeps
the colorless cow swims soothingly
the bagel swims intentionally
a idea sprints
the colorless colorless colorless green cow swims intentionally
- recall that the range function that we use in for loops (like above), is really just generating a list that we then iterate over:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- besides this version, range also has two other variants (one of which we haven't seen)
- we can optionally give it a starting point, instead of starting at 0
>>> range(4, 10)
[4, 5, 6, 7, 8, 9]
- in addition, we can also give it a third parameter that is the increment, instead of just incrementing by 1
>>> range(4, 10, 2)
[4, 6, 8]
- we'd like to write our own version of this function
- the challenge is how to have one function support a differing number of arguments
- Python has optional parameter
- an optional parameter is included in the list of parameters, however, we give it a default value using '='
- the function can be called with OR without that parameter
- if it's called without it, the parameter gets the default value specified by '='
- it it's called with it, the parameter gets whatever the user passes in
- look at the my_range function in
- the function takes three parameters: start, end, step
- we generate the list of values by starting with an empty list and append on the values
- we set i to start out at the start
- as long as (while) i is less than the end
- append i to the list
- increase i by the step size
- my range has step as optional
- if step isn't specified, it gets a value of 1
- if it is, then we use the value specified
>>> my_range(0, 10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> my_range(0, 10, 2)
[0, 2, 4, 6, 8]
- optional parameters must come AFTER all non-optional parameters. why?
- say we defined a function as follows:
def simple(a = 1, b = 2, c):
what happens if I call it with 2 parameter?
>>> simple(10, 15)
- we don't know whether a or b should get 10
parameters by value
- look at the optional function in
- what does this function do?
- how can we call it?
>>> optional(10, 11)
>>> optional(10, 11, 2)
- what if we wanted to specify multiplier, but not adder?
>>> optional(10, multiplier=10)
- Python allows you to specify ANY parameter by name
- if you have optional parameters, you can specify them by name
- you can also specify required parameters by name
>>> optional(multiplier=2, val = 10)