Oulipo
String and dance.
Oulipo gives you tools to write constrained stories and poems with Ruby.
It's still young, and very much liable to change.
The real Oulipo is a gathering of writers and mathmeticians who seek to create works using constrained writing techniques.
Install with gem install oulipo
and require 'oulipo'
as needed.
Lipograms and Pangrams
Oulipo can detect lipograms and pangrams.
A lipogram willfully shuns one or more letters of the alphabet.
In the following snippet of a poem, every letter of the alphabet except 'e' is used:
stanza = <<-GYLES_BRANDRETH
Not work of man, nor sport of child
Finds Nassan on this mazy wild;
Lax grow his joints, limbs toil in vain--
Poor wight! why didst thou quit that plain?
GYLES_BRANDRETH
Oulipo.lipogram?(stanza) # => true
Oulipo.absent_letters(stanza) # => ['e']
In contrast, a pangram uses all the letters of the alphabet (often seen jumping lazy dogs):
sentence = 'Big fjords vex quick waltz nymph.'
Oulipo.pangram?(sentence) # => true
The prisoner's constraint, also known as the Macao constraint, is a type of lipogram in which no letters can be ascenders (b, d, f, g, h, j, k, l) or descenders (p, q, t, y).
line = "rain's music runs in rivers"
Oulipo.prisoner?(line) # => true
Univocalims
A univocalism is a poem written using only one type of vowel.
poem = <<-POEM
No cool monsoons blow soft on Oxford dons,
Orthodox, jog-trot, book-worm Solomons
POEM
Oulipo.univocalism?(poem) # => true
Palindromes
Palindromes read the same way, backwards or forwards:
Oulipo.palindrome?('Eva, can I stab bats in a cave?') # => true
Chaterisms
A chaterism is a poem where either each successive word in the poem grows by one letter (also known as "snowball poem") or shrinks by one letter.
Oulipo.chaterism? 'Ruby loves poetry!' # => true
Oulipo.chaterism? 'Poetry loves Ruby, too.' # => true
poem = <<-WORDS
One
poem
grows,
author
watches,
helpless --
syllables
accumulate.
WORDS
Oulipo.snowball?(poem) # => true
Alliteration
Oulipo can tell you about alliterations.
Oulipo.alliteration? 'ravenous Ruby relishes radical raconteurs' # => true
Normal alliteration's a little harsh, so you can give it a threshold, too.
phrase = 'quick queens quibble over quails'
Oulipo.alliterativity(phrase) # => 0.8 (4/5 words start with 'q')
Oulipo.alliteration?(phrase, :threshold => 0.7) # => true
Oulipo.alliteration?(phrase, :threshold => 0.9) # => false
N+7
In N+7 (sometimes known as S+7), each noun in a text is replaced with the noun seven entries after it in a dictionary.
dictionary = Oulipo::WordList.load('big_list_of_nouns.txt')
play = <<-SHAKESPEARE
What, jealous Oberon! Fairies, skip hence:
I have forsworn his bed and company.
SHAKESPEARE
Oulipo.n_plus(7, play, dictionary) # => "What, jealous Oberon! Fallacies, skulk hence:
# I have forsworn his bedroom and compensation."
Oulipo includes a handy WordList
class for reading one-word-per-line dictionary files, but a dictionary can be any object that responds to index(word)
, length
, and [index]
.
dictionary = %w{ iron gild mine gold ore paint cast lily }
king_john = 'To gild refined gold, to paint the lily'
Oulipo.n_plus(1, king_john, dictionary) # => 'To mine refined ore, to cast the iron'
String Extensions
You can optionally extend String
, if you need to.
require 'oulipo/string_em_up'
"Sator arepo tenet opera rotas".palindrome? # => true
"Waltz, bad nymph, for quick jigs vex!".pangram? # => true
'To gild refined gold'.n_plus(7, nouns) # ... as above
If you'd like to use the neat short-hand, but don't want to touch String
, you might want to use an EnhancedString
.
palindrome = Oulipo::EnhancedString.new("Sator arepo tenet opera rotas")
palindrome.lipogram? # => true
Analysis
Rudimentary analysis can be performed by using Oulipo's Analysis
class.
line = 'A rose by any other name would smell as sweet'
word_sets = {
:nouns => %w{ bear name rose },
:adjectives => %w{ sweet }
}
analysis = Oulipo::Analysis.new(line, word_sets)
analysis.identified(:nouns) # => ['rose', 'name']
analysis.identified(:adjectives) # => ['sweet']
analysis.deconstruction # => ["A ", ["rose", :nouns], " by any other ", ["name", :nouns], " would smell as ", ["sweet", :adjectives]]
Substitution
Substitution can be performed on an analysis.
Carrying on from the example above:
substitutor = Oulipo::Substitutor.new(analysis)
substitutor.replace(:nouns).increment(1) # => "A bear by any other rose would smell as sweet"
Pete Nicholls (@Aupajo)