Des livres pleins de bêtises: Identifiez les limericks

15

Comme nous le savons tous, les limericks sont de courts poèmes de cinq lignes, parfois obscènes, avec un schéma de rimes AABBA et un mètre anapestic (quel qu'il soit):

Écriture absurde d'un Limerick
Ligne un et ligne cinq rime dans le mot
Et tout comme vous l'avez compté
Ils riment avec le second
La quatrième ligne doit rimer avec le troisième

Vous êtes chargé d'écrire le programme le plus court qui, lorsqu'il est alimenté en texte d'entrée, imprime s'il pense que l'entrée est un limerick valide. L'entrée peut être sur la ligne de commande ou via une entrée standard, à votre choix, et la sortie peut être soit un simple "Y" / "N" ou un score de confiance, encore une fois à votre choix.

Voici un autre exemple de limerick correct:

Il y avait une jeune femme dont les yeux
étaient uniques quant à la couleur et à la taille
Quand elle les ouvrit largement Les
gens se détournèrent tous
Et partirent surpris

Mais le poème ci-dessous n'est clairement pas un limerick, car il ne rime pas:

Il y avait un vieil homme de St. Bees
qui a été piqué au bras par une guêpe.
Lorsqu'on lui a demandé: "Est-ce que ça fait mal?"
Il a répondu: "Non, ce n'est pas le cas,
je suis tellement content que ce ne soit pas un frelon."

Ce n'est pas non plus celui-ci, car le compteur est tout faux:

J'ai entendu d'un homme de Berlin
qui détestait la chambre , il était en
Quand j'ai demandé la raison pour laquelle
il disait avec un soupir:
« Eh bien, voyez - vous, hier soir , il y avait deux truands autour qui célébraient les Bears gagner le reprisés Coupe du monde, et ils étaient vraiment bruyants, donc je n'ai pas pu dormir à cause du vacarme. "

Des indices

Voici quelques indices que vous pourriez utiliser pour décider si votre contribution est un limerick ou non:

  • Les limericks ont toujours cinq lignes.
  • Les lignes 1, 2 et 5 doivent rimer.
  • Les lignes 3 et 4 doivent rimer.
  • Les lignes 1, 2 et 5 ont environ 3x3 = 9 syllabes, tandis que les troisième et quatrième ont 2x3 = 6 syllabes

Notez qu'aucun de ceux-ci sauf le premier n'est dur et rapide: un taux d'exactitude de 100% est impossible.

Règles

  • Votre entrée doit au moins correctement classer les exemples 1 à 3 de manière déterministe.

  • Vous êtes autorisé à utiliser n'importe quel langage de programmation que vous souhaitez, à l'exception bien sûr des langages de programmation spécialement conçus pour ce concours (voir ici ).

  • Vous n'êtes pas autorisé à utiliser une bibliothèque à l'exception des offres standard de votre langage de programmation.

  • Vous êtes autorisé à supposer que ce fichier , le dictionnaire de prononciation CMU Sphinx, se trouve dans un fichier appelé «c» dans le répertoire actuel.

  • Vous n'êtes pas autorisé à coder en dur pour les entrées de test: votre programme devrait être un catégoriseur général limerick.

  • Vous êtes autorisé à supposer que l'entrée est ASCII, sans mise en forme spéciale (comme dans les exemples), mais votre programme ne doit pas être confondu par interpunction.

Bonus

Les bonus suivants sont disponibles:

  • Votre programme sort son résultat sous forme de limerick? Soustrayez un bonus de 150 caractères !
  • Votre programme identifie également correctement les sonnets? Soustrayez 150 caractères de bonus de longueur supplémentaire!
  • Votre programme génère son résultat sous forme de sonnet lorsqu'il est utilisé sur un sonnet? Soustrayez 100 caractères de bonus supplémentaire de longueur supplémentaire!

Finalement...

N'oubliez pas de mentionner les bonus que vous pensez mériter, le cas échéant, et soustrayez le bonus de votre nombre de personnages pour arriver à votre score. Il s'agit d'un concours de golf à code : l'entrée la plus courte (c'est-à-dire l'entrée avec le score le plus bas) gagne.

Si vous avez besoin de plus de données de test (positives), consultez l' OEDILF ou le Book of Nonsense . Les données de test négatives devraient être faciles à construire.

Bonne chance!

Wander Nauta
la source
Cela devrait être code-challengedû aux bonus. Veuillez lire les descriptions des balises
user80551
2
@ user80551 Le consensus sur la méta semble être différent.
Poignée de porte
J'ai clarifié la nature des bonus, j'espère que cela clarifie la confusion.
Wander Nauta
2
Goooooooo Bears!
alvonellos
Je ne comprends pas les bonus. Comment suis-je censé sortir "Y" sous la forme d'un limerick?
squeamish ossifrage

Réponses:

8

Python: 400-150-150 = 100

Le script le plus court que j'ai pu trouver est celui-là ...

import re,sys;f,e,c=re.findall,lambda l,w:f('^'+w.upper()+'  (.+)',l),lambda*v:all([a[i]==a[v[0]]for i in v]);a=[sum([[e(l,w)[0].split()for l in open('c')if e(l,w)][0]for w in f(r'\w+',v)],[])[-2:]for v in sys.stdin];n=len(a);print n==14and c(0,3,4,7)*c(1,2,5,6)*c(8,11)*c(9,12)*c(10,13)*"Sonnet"or"For a critic\nOf limerick\nWell-equipped\nIs this script.\n%s limerick!"%(n==5and c(0,1,4)and c(2,3))

... mais ne l'essayez même pas. Il analyse le dictionnaire fourni pour chaque mot qu'il rencontre, ce qui est donc très lent. En outre, une erreur est générée chaque fois qu'un mot n'est pas dans le dictionnaire.

Le code répond néanmoins aux exigences: reconnaître si le texte passé via stdin est un limerick, un sonnet ou aucun de ceux-ci.

Avec seulement 20 caractères supplémentaires, voici la version optimisée:

import re,sys;f,e,c=re.findall,lambda l:f(r'^(\w+)  (.+)',l),lambda*v:all([a[i]==a[v[0]]for i in v]);d={e(l)[0][0]:e(l)[0][1].split()for l in open('c')if e(l)};a=[sum([d.get(w.upper(),[])for w in f(r'\w+',v)],[])[-2:]for v in sys.stdin];n=len(a);print n==14and c(0,3,4,7)*c(1,2,5,6)*c(8,11)*c(9,12)*c(10,13)*"Sonnet"or"For a critic\nOf limerick\nWell-equipped\nIs this script.\n%s limerick!"%(n==5and c(0,1,4)and c(2,3))

traits

  • capable de reconnaître les sonnets (-150)
  • réponses aux limericks avec un limerick (-150)
  • relativement rapide: un seul fichier analysé par exécution

Usage

cat poem.txt | python poem-check.py

3 sorties différentes sont possibles:

  • un limmerick disant que l'entrée est une si c'est le cas
  • un limmerick disant que l'entrée n'en est pas une si c'est le cas
  • "Sonnet" si l'entrée est reconnue comme telle

Code développé avec explications

import re, sys

# just a shortened version of the 're.findall' function...
f = re.findall
# function used to parse a line of the dictionary
e = lambda l:f(r'^(\w+)  (.+)', l)

# create a cache of the dictionary, where each word is associated with the list of phonemes it contains
d = {e(l)[0][0]:e(l)[0][1].split(' ') for l in open('c') if e(l)}

# for each verse (line) 'v' found in the input 'sys.stdin', create a list of the phoneme it contains;
# the result array 'a' contains a list, each item of it corresponding to the last two phonemes of a verse
a = [sum([d.get(w.upper(), []) for w in f(r'\w+',v)],[])[-2:] for v in sys.stdin]

# let's store the length of 'a' in 'n'; it is actually the number of verses in the input
n = len(a)
# function used to compare the rhymes of the lines which indexes are passed as arguments
c = lambda*v:all([a[i] == a[v[0]] for i in v])

# test if the input is a sonnet, aka: it has 14 verses, verses 0, 3, 4 and 7 rhyme together, verses 1, 2, 5 and 6 rhyme together, verses 8 and 11 rhyme together, verses 9 and 12 rhyme together, verses 10 and 13 rhyme together
if n==14 and c(0,3,4,7) and c(1,2,5,6) and c(8,11) and c(9,12) and c(10,13):
    print("Sonnet")
else:
    # test if the input is a limerick, aka: it has 5 verses, verses 0, 1 and 4 rhyme together, verses 2 and 3 rhyme together
    is_limerick = n==5 and c(0,1,4) and c(2,3)
    print("For critics\nOf limericks,\nWell-equipped\nIs this script.\n%s limerick!", is_limmerick)
Mathieu Rodic
la source
Ça a l'air cool! Je ne l'ai pas encore testé, mais êtes-vous sûr que cela prend une entrée "soit sur la ligne de commande ou via une entrée standard" (voir question)? Sinon, vous devez ajouter cela (probablement un sys.stdin.read()ou un open(sys.argv[1]).read()quelque part) et recompter.
Wander Nauta
D'accord! Corrigé :)
Mathieu Rodic
Comment l'algorithme vérifie-t-il les rimes?
DavidC
Avec l'aide du fichier fourni par Wander Nauta dans la question! Ça m'a vraiment aidé.
Mathieu Rodic
1
Soigné! Dommage que je ne puisse pas vous voter deux fois.
Wander Nauta
2

ECMAScript 6 (138 points; essayez dans Firefox):

288- 150bonus de points pour inclure limerick (extrait de @MathieuRodic).

a=i.split(d=/\r?\n/).map(x=>x.split(' '));b=/^\W?(\w+) .*? (\w+\d( [A-Z]+)*)$/;c.split('\r\n').map(x=>b.test(x)&&eval(x.replace(b,'d["$1"]="$2"')));e=f=>d[a[f][a[f].length-1]];alert('For critics\nOf limericks,\nWell-equipped\nIs this script.\n'+(a[4]&&e(0)==e(1)&e(0)==e(4))+' limerick!')

Remarques:

Attend la variable c contienne le contenu du fichier de dictionnaire, car vous ne pouvez pas lire les fichiers en ECMAScript simple.

ECMAScript n'a pas d'entrée standard, mais promptest généralement considéré comme "entrée standard"; cependant, comme promptconvertit les sauts de ligne en espaces dans la plupart des navigateurs (sinon tous), j'accepte les entrées de la variablei .

Code non golfé:

// If you paste a string with multiple lines into a `prompt`, the browser replaces each line break with a space, for some reason.
//input = prompt();

// Split into lines, with each line split into words
lines = input.split('\n').map(x => x.split(' '));

dictionaryEntryRegEx = /^\W?(\w+) .*? (\w+\d( [A-Z]+)*)$/;
dictionary = {};
// Split it into
c.split(/\r?\n/).map(x => dictionaryEntryRegEx && eval(x.replace(dictionaryEntryRegEx, 'dictionary["$1"] = "$2"')));

// Get the last word in the line
getLastWordOfLine = (lineNumber) => dictionary[line[lineNumber][line[lineNumber].length - 1]]

alert('For critics\nOf limericks,\nWell-equipped\nIs this script.\n' + (lines[4] && getLastWordOfLine(0) === getLastWordOfLine(1) && getLastWordOfLine(0) === getLastWordOfLine(4)) + ' limerick!');
Brosse à dents
la source
Soigné! Cela ne prend pas 'd'entrée sur la ligne de commande ou via une entrée standard', cependant, ce qui est requis par la question. Vous pourriez peut-être le réécrire pour utiliser Node.js ou quelque chose.
Wander Nauta
@WanderNauta Merci. Veuillez consulter la dernière modification, car j'explique pourquoi je n'utilise pas l'entrée standard.
Brosse à dents du