Livros cheios de bobagens: Identifique limericks

15

Como todos sabemos, os limericks são poemas curtos, de cinco linhas e ocasionalmente lascivos, com um esquema de rima AABBA e um medidor anapésico (o que quer que seja):

Escrevendo a absurda
linha de Limerick, linha um e linha cinco, rima em palavras
E assim como você calculou
Eles rimam com o segundo
A quarta linha deve rimar com o terceiro

Você tem a tarefa de escrever o programa mais curto que, quando alimentado um texto de entrada, imprime se considera que a entrada é uma limerick válida. A entrada pode estar na linha de comando ou através da entrada padrão, a seu critério, e a saída pode ser um simples "Y" / "N" ou uma pontuação de confiança, novamente à sua opção.

Aqui está outro exemplo de uma limerick correta:

Havia uma jovem cujos olhos
eram únicos quanto à cor e tamanho
Quando os abriu largamente
As pessoas se viraram
e começaram a se surpreender

Mas o poema abaixo claramente não é uma limerick, pois não rima:

Havia um homem velho de abelhas
que foi picado no braço por uma vespa.
Quando perguntado: "Dói?"
Ele respondeu: "Não, não,
estou tão feliz que não tenha sido uma vespa".

Nem é este, pois o medidor está errado:

Ouvi falar de um homem de Berlim
que odiava o quarto em que estava.
Quando perguntei por que
ele diria com um suspiro:
"Bem, veja bem, ontem à noite havia alguns bandidos que estavam comemorando os ursos vencendo os condenados. Copa do Mundo, e eles estavam muito barulhentos, então eu não consegui dormir por causa do barulho ".

Pistas

Aqui estão algumas das dicas que você pode usar para decidir se sua entrada é ou não uma limerick:

  • Limericks sempre têm cinco linhas.
  • As linhas 1, 2 e 5 devem rimar.
  • As linhas 3 e 4 devem rimar.
  • As linhas 1, 2 e 5 têm cerca de 3x3 = 9 sílabas, enquanto a terceira e quarta têm 2x3 = 6 sílabas

Observe que nenhum desses, exceto o primeiro, é fácil e rápido: uma taxa de correção de 100% é impossível.

Regras

  • Sua entrada deve, no mínimo, categorizar corretamente os exemplos 1 a 3 de maneira determinística.

  • Você tem permissão para usar qualquer linguagem de programação que desejar, exceto, é claro, as linguagens de programação projetadas especificamente para este concurso (veja aqui ).

  • Você não tem permissão para usar nenhuma biblioteca, exceto as ofertas padrão da sua linguagem de programação.

  • Você está autorizado a supor que esse arquivo , o dicionário pronounciation CMU Sphinx, está em um arquivo chamado 'c' no diretório atual.

  • Você não tem permissão para codificar para as entradas de teste: seu programa deve ser um categorizador geral de limerick.

  • Você tem permissão para assumir que a entrada é ASCII, sem nenhuma formatação especial (como nos exemplos), mas seu programa não deve ser confundido pela interpunção.

Bónus

Os seguintes bônus estão disponíveis:

  • Seu programa gera seu resultado como uma limerick? Subtrair bônus de comprimento de 150 caracteres !
  • Seu programa também identifica corretamente sonetos? Subtrair bônus de comprimento extra de 150 caracteres !
  • Seu programa gera seu resultado como um soneto quando usado em um soneto? Subtraia 100 caracteres de bônus de comprimento extra adicional!

Finalmente...

Lembre-se de mencionar quais bônus você acha que merece, se houver, e subtrair o bônus do seu número de caracteres para chegar à sua pontuação. Este é um concurso de código de golfe : a entrada mais curta (ou seja, a entrada com a menor pontuação) vence.

Se você precisar de mais dados de teste (positivos), consulte o OEDILF ou o Book of Nonsense . Dados de teste negativos devem ser fáceis de construir.

Boa sorte!

Wander Nauta
fonte
Isso deve ser code-challengedevido aos bônus. Por favor, leia as descrições das tags
user80551 22/03
2
@ user80551 O consenso sobre meta parece ser diferente.
Maçaneta
Esclareci a natureza dos bônus, espero que isso esclareça a confusão.
Wander Nauta
2
Goooooooo ursos!
Alvonellos
Eu não entendo os bônus. Como devo imprimir "Y" na forma de uma limerick?
Ossifrage melindroso 24/03

Respostas:

8

Python: 400-150-150 = 100

O script mais curto que eu poderia inventar é aquele ...

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

... mas nem tente. Ele analisa o dicionário fornecido para cada palavra que encontra, sendo muito lento. Além disso, um erro é gerado sempre que uma palavra não está no dicionário.

O código ainda atende aos requisitos: reconhecer se o texto passado via stdin é uma limerick, um soneto ou nenhum deles.

Com apenas mais 20 caracteres, aqui está a versão otimizada:

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

Recursos

  • capaz de reconhecer sonetos (-150)
  • respostas a limericks com uma limerick (-150)
  • relativamente rápido: apenas um arquivo analisando por execução

Uso

cat poem.txt | python poem-check.py

São possíveis 3 saídas diferentes:

  • um limmerick dizendo que a entrada é uma, se for o caso
  • um limmerick dizendo que a entrada não é uma, se for o caso
  • "Sonnet" se a entrada for reconhecida como tal

Código expandido com explicações

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
fonte
Parece legal! Ainda não testei, mas você tem certeza de que isso recebe a entrada "na linha de comando ou através da entrada padrão" (consulte a pergunta)? Caso contrário, você deve adicionar isso (provavelmente um sys.stdin.read()ou um open(sys.argv[1]).read()lugar) e recontar.
Wander Nauta
OK! Corrigido :) #
Mathieu Rodic 22/03
Como o algoritmo verifica rimas?
23414
Com a ajuda do arquivo fornecido por Wander Nauta na pergunta! Isso realmente ajudou.
Mathieu Rodic
1
Arrumado! Uma pena eu não poder te votar duas vezes.
Wander Nauta
2

ECMAScript 6 (138 pontos; tente no Firefox):

288- 150bônus de pontos por incluir limerick (extraído 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 a variável ccontenha o conteúdo do arquivo de dicionário, pois você não pode ler arquivos no ECMAScript simples.

O ECMAScript não possui entrada padrão, mas promptgeralmente é considerado "entrada padrão"; no entanto, como promptconverte quebras de linha em espaços na maioria dos navegadores (se não todos), estou aceitando a entrada da variável i.

Código não destruído:

// 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!');
Escova de dente
fonte
Arrumado! Porém, isso não recebe 'entrada na linha de comando ou através da entrada padrão', o que é exigido pela pergunta. Talvez você possa reescrevê-lo para usar o Node.js ou algo assim.
Wander Nauta
@WanderNauta Obrigado. Veja a edição mais recente, pois explico por que não estou usando a entrada padrão.
Escova de dentes