| Second Lisp Assignment: Random sentences Dave Matuszek |
Your assignment is to write a Lisp program to generate random sentences.
You should have a function named GEN that takes an integer argument
N, and does two things:
N randomly generated sentences, one per line.In Lisp it is better to write lots of small functions than a few big ones.
For example, GEN could print out your name and call a second function
to do the rest of the work. This second function might recur on N
(to get N sentences), call a third function to generate and return
one sentence, and print out that sentence. Typically, you would put all your
functions in the same .lsp file.
You can generate sentences using a simple context-free grammar. Here are typical abbreviations for a grammar:
| Abbreviation | Stands for | Examples |
S |
sentence | the boy sees the big red hairy red troll |
N |
noun | boy, ring, hobbit, troll, moon, telescope |
V |
verb | hits, runs, sees |
ADJ |
adjective | big, red, hairy |
ADV |
adverb | quickly, quietly, |
DET |
determiner | a, an, the, this, that, each, every |
PRON |
pronoun | he, she, it |
PREP |
preposition | in, on, around, about |
CONJ |
conjunction | and, but |
NP |
noun phrase | the boy, the man in the moon |
VP |
verb phrase | runs, hits the tree, sees the moon with the telescope |
PP |
prepositional phrase | in the moon, with the telescope |
In a context-free grammar, these abbreviations would be the nonterminals (things that need to be expanded), and the English words that they expand into are the terminals (because you stop there and don't expand them any further). When you write your program, please start with these abbreviations, but feel free to ignore some of them and/or add new ones of your own. Please use your own vocabulary (words), not just the ones above.
Here are some very simple rules (the vertical bar, |, means "or"--choose
just one of the alternatives). Again, start with these, but feel free to add/modify/delete
rules as you choose.
S --> NP VPN --> BOY | RING | HOBBIT | TROLL | MOON
| TELESCOPEV --> HITS | RUNS | SEESADJ --> BIG | RED | HAIRYADV --> QUICKLY | QUIETLYDET --> A | AN | THE | THIS | THAT | EACH
| EVERYPRON --> HE | SHE | ITPREP --> IN | ON | AROUND | ABOUTCONJ --> AND | BUTNP --> DET N | DET N PP | DET ADJLIST NVP --> V | V NPPP --> PREP NPADJLIST --> ADJ | ADJ ADJLISTHere are some things you need to pay attention to:
NP and ADJLIST, can allow this to happen.
Adjust your probabilities so that very long sentences are possible, but unlikely.Here are some things you should not worry about:
(the boy sees the big red hairy red troll), and that's fine. Get rid
of inner parentheses, though: don't generate ((the boy) (sees (the (big
(red (hairy (red troll)))))))!Here are some functions you might find useful (the ones without a defun
are built-in functions):
(defun outof (m n)(< (random n) m)) |
Returns true m times out of n; for example,
(outof 1 10) will return true one time in ten (and
NIL the rest of the time). If m >= n, outof
always returns true. |
(defun choose (L) |
Returns one randomly chosen element of the list. For example, (choose '(a (b c) (d e) f) )
might return (d e). |
(prin1 S) |
Print the S-expression S, without a newline after it. Like
System.out.print(S) in Java. |
(print S) |
Print the S-expression S, followed by a newline. Like System.out.println(S)
in Java. |
(terpri) |
Print a newline. Like System.out.println() in Java. |
I have gotten questions about how to put the rules into Lisp. One simple way is to define them as functions: for example,
(defun pron () '(he she it))
(defun np () '((det n) (det n pp) (det adjlist n)))
Another way is to use setq to make them "constants," for example:
(setq adj '(big red hairy))
(setq vp '(v (v np)))
The first way, you would call them as functions; for example, (pron)
evaluates to (he she it). The second way, you would use
them as variables, for example, adj evaluates to (big red
hairy). Either way will work fine. There are two special forms that are
especially convenient in this program:
(eval S) |
Evaluates the expression S. For example, (eval '(car '(a b c)))
evaluates the quoted expression (car '(a b c)),
giving a. This function is especially useful if you stored
your rules using setq. |
(apply fun args) |
Calls the function fun with the argument list args.
For example, (apply 'cons '(a (b c))) gives
(a b c). This function is especially useful if you
stored your rules as functions. |
Example of using eval with setq |
Example of using apply with defun |
> (setq adj '(big strong hairy))(big strong hairy)Now suppose x has the value 'adj> x Then |
|
If you are having trouble getting started, here are some suggestions.
You will need to distinguish between nonterminals (S, NP,
...) from terminals (BOY, EACH, ...). One way to make
this distinction is to write a function that, given an atom argument, tests
whether that atom is in a list of nonterminals. (It's easier to make a list
of nonterminals than a list of terminals, because (1) there are fewer nonterminals,
and (2) you are more likely to want to add/change nonterminals (words) than
terminals (grammatical categories).
Start with the list '(S), because you want to generate sentences.
Write a recursive function (I called mine expand) that finds a
rule for each nonterminal and replaces (expands) it. Some rules have only a
single alternative, so use that alternative; for example PP --> PREP NP
gives you no choice. Some rules have multiple alternatives, such as
V --> V | V NP, so use my choose
or some similar function to pick one alternative. (By the way, if you give my
choose function a list containing only one thing, it picks that one thing, so
you don't need to make a special case.)
If you look at the rules above, S --> NP VP says to replace
S with NP VP, so change your list from (S) to
(NP VP). Next, NP is still a nonterminal, NP --> DET
N | DET N PP | DET ADJLIST N, so you need to choose any one of these
alternatives, say, DET N PP, and change your list from
(NP VP) to (DET N PP VP). Continue
in this fashion until you get a list composed entirely of terminals.
Here's a possible sequence (terminals are shown in blue):
| Start with: | (S) |
Use the rule S --> NP VP |
(NP VP) |
Use the rule NP --> DET N PP |
(DET N PP VP) |
Use the rule DET --> EACH |
(EACH N PP VP) |
Use the rule N --> BOY |
(EACH BOY PP VP) |
Use the rule PP --> PREP NP |
(EACH BOY PREP NP VP) |
Use the rule PREP --> IN |
(EACH BOY IN NP VP) |
Use the rule NP --> DET N |
(EACH BOY IN DET N VP) |
| Continue... | ... |
| ...until you have only terminals left: | (EACH BOY IN EVERY RING SEES) |
Basically, every time you have a nonterminal, you need to call a recursive function to turn it into a list of terminals. Putting all these terminals together gives you the final list.
Due date: Thursday, January 24, by midnight. Please submit one
.lsp file containing all your functions via Blackboard.