Skip to content

Instantly share code, notes, and snippets.

@kerryrodden
Created January 18, 2019 22:34
Show Gist options
  • Save kerryrodden/9ea288f72b1c30b9c8387ee5b65f8f89 to your computer and use it in GitHub Desktop.
Save kerryrodden/9ea288f72b1c30b9c8387ee5b65f8f89 to your computer and use it in GitHub Desktop.

What this code does and why

This code makes a "poem" via choosing lines at random from a book of phrases - in particular, it uses "Fifteen Thousand Useful Phrases" by Grenville Kleiser, which it downloads from Project Gutenberg.

Inspired by other computer-generated short texts, like Allison Parrish's Twitter bots, I was curious whether I could generate some poetry that seemed vaguely plausible, purely by using a random approach - without having to do any natural language processing or machine learning. To do that I needed a text to choose lines from, where the individual lines were already readable English, and would have a reasonable chance of making some sense when rearranged at random.

Interesting outputs

Here are a few example outputs that I thought could pass as human-written poetry (not necessarily good poetry):

There is certainly no reason
Yielding like melted snow
unfulfilled longing
This being undeniable, it is plain
I wonder if you have the smallest recollection of me?
tractable, gentle, pliant, and submissive
The earth looked despoiled
A narrow and superficial survey
stripped, swept, and bare
Ought we not to think
Whatever the truth may be
Is it not, then, preposterous
As iridescent as a soap bubble
Hoping for a continuance of your interest
decadent poets
lowering aspects
ascertain with exactness

Others connected lines together in a way that was accidentally meaningful. This one seems to tell a small story:

A certain implication of admiring confidence
I see disapproval in your face
I am more grieved than I can tell you
I must conclude abruptly

In this one, I like that the line referring to "him" is followed by one that starts with "he", and that "paralyzing doubts" is followed by "would you mind telling me your opinion?"

My appreciation has been quickened
All the unknown of the night and of the universe was pressing upon him
He threw a ton's weight of resolve upon his muscles
realistic and effective
I am also satisfied
Paralyzing doubts and scruples
Would you mind telling me your opinion?

Here, I like how line 2 seems to complete line 1:

The curling wreaths like turbans seem
ancient and venerable
I am glad to say that I have entirely lost that faculty
It must be a cause of delight

In this one, line 2 creates context through juxtaposition, changing the meaning of lines 1 and 3.

I beg again to thank you for the honor
Her scarlet lip curled cruelly
Don't let me encroach on your good nature

Finally, some examples were unintentionally humorous. In particular, there is a section of the book with "business phrases", intended for formal letter-writing, and I love when those get mixed in with more flowery language from other sections of the book.

Screen themselves from punishment
vehement panting
An addressed envelope is enclosed for your convenience
unnecessary platitudes
fine sensibilities
I am not unaware
merge into character
Thanking you for your inquiry
I have always maintained
Like two flaming stars were his eyes
I am sure, at any rate
An essentially grotesque and commonplace thing

Limitations of this approach

Most of the outputs are plausible at first glance but then don't really make sense when read through. This is the first poem I generated, which is representative:

harsh and austere
I am very sure that if you ponder
doleful forebodings
I don't deny that it is interesting
irritable, choleric, petulant, and susceptible

Many are overwrought, clumsy, or simply have too many metaphors and similes for one poem.

I dare say you know
illustrative anecdote
Like some grave night thought threading a dream
Struck by a sudden curiosity
I ask your attention
The marvelous beauty of her womanhood
mastery, proficiency, dexterity, and superiority
and all the earth revealeth
Nothing could be more striking
shouts of approval
Her voice like mournful bells crying on the wind
Fed by many currents from the long stream of human experience
like a silvery, shimmering shower of hail
In a flash of revelation

Implementation details

The "Fifteen Thousand Useful Phrases" book is structured in such a way that it is relatively easy to avoid choosing lines from the introduction or other boilerplate, simply by ignoring the beginning and end of the file.

Similarly, within the core of the book, anything that is not a phrase can be stripped out by removing any text in square brackets (which are definitions added by the transcriber), and then removing lines with no text (or only 1 character) and lines that are in ALL CAPS (which are section headings).

Some of the original phrases are split across two lines, sometimes with a comma at the end of the first line, so I also strip off any trailing commas before printing.

# Makes a "poem" via choosing lines at random from a book of phrases
# Uses Python3
# By kerryrodden, January 2019
import random
import re
import requests
# "Fifteen Thousand Useful Phrases" by Grenville Kleiser, from Project Gutenberg
BOOK_URL = "http://www.gutenberg.org/cache/epub/18362/pg18362.txt"
# The beginning and end of the actual list of phrases (not the introduction or other boilerplate)
START_LINE = 507
END_LINE = 33000
# The desired minimum and maximum length of the generated poem
MIN_LENGTH = 2
MAX_LENGTH = 7
# Fetch the book from its URL
response = requests.get(BOOK_URL)
full_text = response.text
# Remove any text that is in square brackets (these are definitions added by the transcriber)
no_definitions = re.sub("\[.*?\]", "", full_text)
# Get the text as a list of individual lines
lines = no_definitions.splitlines()
# Extract the core of the book (not the introduction or other boilerplate)
core = lines[START_LINE:END_LINE]
# Function to filter out any lines that are blank or have only 1 character, and lines in all caps (section headings)
def filter_lines(line):
return len(line.strip()) > 1 and not line.isupper()
phrases = list(filter(filter_lines, core))
phrase_count = len(phrases)
# Choose a length for the poem
poem_length = random.randint(MIN_LENGTH, MAX_LENGTH)
# Select that many lines at random from the list of phrases, and print them
for i in range(poem_length):
index = random.randrange(phrase_count)
line = phrases[index].rstrip(",") # remove any trailing comma from the line
print(line)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment