Libros llenos de tonterías: identificar limericks

15

Como todos sabemos, los limericks son poemas cortos, de cinco líneas, ocasionalmente lascivos, con un esquema de rima AABBA y un medidor anaplástico (sea lo que sea):

Escribir un absurdo de Limerick La
línea uno y la línea cinco riman en palabras
Y tal como has calculado
Ellos riman con el segundo
La cuarta línea debe rimar con el tercero

Se le asigna la tarea de escribir el programa más corto que, cuando alimenta un texto de entrada, imprime si cree que la entrada es un limerick válido. La entrada puede estar en la línea de comando o mediante la entrada estándar, a su elección, y la salida puede ser una simple "Y" / "N" o un puntaje de confianza, nuevamente a su elección.

Aquí hay otro ejemplo de un limerick correcto:

Había una señorita cuyos ojos
eran únicos en cuanto a color y tamaño.
Cuando los abrió de par en par. La
gente se apartó
y se alejó sorprendida.

Pero el siguiente poema claramente no es un limerick, ya que no rima:

Había un anciano de St. Bees
que fue picado en el brazo por una avispa.
Cuando se le preguntó: "¿Duele?"
Él respondió: "No, no lo hace,
estoy muy contento de que no haya sido un avispón".

Tampoco es este, ya que el medidor está todo mal:

Escuché de un hombre de Berlín
que odiaba la habitación en la que se encontraba.
Cuando le pregunté por qué
diría con un suspiro:
"Bueno, ya ves, anoche hubo un par de matones que celebraban a los Osos ganando a los zurdos Copa del Mundo, y eran muy ruidosos, así que no pude dormir por el estruendo ".

Pistas

Estas son algunas de las pistas que podría usar para decidir si su aporte es o no un limerick:

  • Los Limericks siempre tienen cinco líneas de largo.
  • Las líneas 1, 2 y 5 deben rimar.
  • Las líneas 3 y 4 deben rimar.
  • Las líneas 1, 2 y 5 tienen alrededor de 3x3 = 9 sílabas, mientras que la tercera y la cuarta tienen 2x3 = 6 sílabas

Tenga en cuenta que ninguno de estos, excepto el primero, es duro y rápido: una calificación de corrección del 100% es imposible.

Reglas

  • Su entrada debe, como mínimo , clasificar correctamente los ejemplos 1 a 3 de manera determinista.

  • Usted está autorizado a utilizar cualquier lenguaje de programación que le gustaría, a excepción de la programación supuesto lenguajes diseñados específicamente para este concurso (ver aquí ).

  • No está autorizado a utilizar ninguna biblioteca, excepto las ofertas estándar de su lenguaje de programación.

  • Usted está permitido suponer que este archivo , el diccionario de pronunciación CMU Sphinx, se encuentra en un archivo llamado 'c' en el directorio actual.

  • Usted está no permite codificar para las entradas de prueba: el programa debe ser un categorizador general de Limerick.

  • Usted está permitido suponer que la entrada es ASCII, sin ningún formato especial (como en los ejemplos), pero su programa no debe ser confundido por interpunction.

Bonos

Los siguientes bonos están disponibles:

  • ¿Su programa genera su resultado como un limerick? ¡Resta el bono de 150 caracteres de longitud!
  • ¿Su programa también identifica correctamente los sonetos? ¡Resta un bono de 150 caracteres de longitud extra!
  • ¿Su programa genera su resultado como un soneto cuando se usa en un soneto? ¡Resta 100 caracteres adicionales de bonificación de longitud extra!

Finalmente...

Recuerde mencionar qué bonos cree que merece, si corresponde, y reste el bono de su número de personajes para llegar a su puntaje. Este es un concurso de código de golf : gana la entrada más corta (es decir, la entrada con la puntuación más baja).

Si necesita más datos de prueba (positivos), consulte el OEDILF o el Libro de las tonterías . Los datos de prueba negativos deberían ser fáciles de construir.

¡Buena suerte!

Wander Nauta
fuente
Esto debería ser code-challengedebido a los bonos. Lea las descripciones de las etiquetas
usuario80551
2
@ user80551 El consenso sobre meta parece ser de otra manera.
Pomo de la puerta
He aclarado la naturaleza de las bonificaciones, espero que eso aclare la confusión.
Wander Nauta
2
¡Goooooooo osos!
alvonellos
No entiendo las bonificaciones. ¿Cómo se supone que debo generar "Y" en forma de limerick?
aprensivo ossifrage

Respuestas:

8

Python: 400-150-150 = 100

El guión más corto que se me ocurrió es ese ...

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))

... pero ni lo intentes. Analiza el diccionario proporcionado para cada palabra que encuentra, por lo tanto, es muy lento. Además, se genera un error cada vez que una palabra no está en el diccionario.

Sin embargo, el código aún cumple con los requisitos: reconocer si el texto pasado a través de stdin es un limerick, un soneto o ninguno de esos.

Con solo 20 caracteres más, aquí está la versión optimizada:

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))

Caracteristicas

  • capaz de reconocer sonetos (-150)
  • respuestas a limericks con un limerick (-150)
  • relativamente rápido: solo un análisis de archivos por ejecución

Uso

cat poem.txt | python poem-check.py

Son posibles 3 salidas diferentes:

  • un limmerick diciendo que la entrada es una si es el caso
  • un limmerick diciendo que la entrada no es una si es el caso
  • "Soneto" si la entrada se reconoce como tal

Código ampliado con explicaciones

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
fuente
¡Se ve bien! Todavía no lo he probado, pero ¿estás seguro de que esto toma la entrada "ya sea en la línea de comando o mediante la entrada estándar" (ver pregunta)? Si no, debe agregar eso (probablemente un sys.stdin.read()o un open(sys.argv[1]).read()lugar) y volver a contar.
Wander Nauta
¡Bueno! Corregido :)
Mathieu Rodic
¿Cómo verifica el algoritmo las rimas?
DavidC
Con la ayuda del archivo proporcionado por Wander Nauta en la pregunta! Realmente ayudó.
Mathieu Rodic
1
¡Ordenado! Una pena que no pueda votarte dos veces.
Wander Nauta
2

ECMAScript 6 (138 puntos; prueba en Firefox):

288- 150bonificación de puntos por incluir limerick (pellizcado 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!')

Notas:

Espera que la variable ccontenga el contenido del archivo de diccionario, ya que no puede leer archivos en ECMAScript simple.

ECMAScript no tiene entrada estándar, pero promptgeneralmente se considera "entrada estándar"; sin embargo, a medida que promptconvierte los saltos de línea en espacios en la mayoría de los navegadores (si no todos), acepto la entrada de la variable i.

Código sin 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!');
Cepillo de dientes
fuente
¡Ordenado! Sin embargo, esto no toma 'entrada en la línea de comando o a través de la entrada estándar', que es requerida por la pregunta. Tal vez podría reescribirlo para usar Node.js o algo así.
Wander Nauta
@WanderNauta Gracias. Consulte la última edición, ya que explico por qué no estoy usando la entrada estándar.
Cepillo de dientes