Crie um índice de legibilidade

13

O algoritmo de legibilidade Flesch-Kincaid depende das medidas de contagem de palavras e contagem de sílabas, nenhuma das quais é totalmente objetiva ou facilmente automatizável usando um computador. Por exemplo, "code-golf", com o hífen, conta como uma palavra ou duas? A palavra "milhão" é duas ou três sílabas? Nesta tarefa, você precisará aproximar, pois a contagem exata levará muito tempo, espaço e, o mais importante, código.

Sua tarefa é criar o menor programa possível (ou seja, uma função) em qualquer idioma que aceite uma passagem de leitura em inglês (presumida em frases completas) e calcular o índice Flesch Reading Ease com uma tolerância de oito pontos (para explicar variações na contagem de sílabas e contagem de palavras). É calculado da seguinte forma:

FRE = 206.835 - 1.015 * (words per sentence) - 84.6 * (syllables per word)

Seu programa deve estar alinhado às passagens de referência abaixo, cujos índices foram calculados usando a contagem manual:

I would not, could not, in the rain.
Not in the dark, not on a train.
Not in a car, not in a tree.
I do not like them, Sam, you see.
Not in a house, not in a box.
Not with a mouse, not with a fox.
I will not eat them here or there.
I do not like them anywhere!

Índice: 111.38 (64 sílabas em 62 palavras em 8 frases)

It was a bright cold day in April, and the clocks were striking thirteen.
Winston Smith, his chin nuzzled into his breast in an effort to escape
the vile wind, slipped quickly through the glass doors of Victory Mansions,
though not quickly enough to prevent a swirl of gritty dust from entering
along with him.

Índice: 65,09 (74 sílabas em 55 palavras em 2 frases)

When in the Course of human events, it becomes necessary for one people to
dissolve the political bands which have connected them with another, and to
assume among the powers of the earth, the separate and equal station to
which the Laws of Nature and of Nature's God entitle them, a decent respect
to the opinions of mankind requires that they should declare the causes
which impel them to the separation.

Índice: 3,70 (110 sílabas em 71 palavras em 1 frase)

Se você tiver outras passagens para as quais tenha contado manualmente as sílabas e palavras e calculado o índice, poderá mostrá-las como verificação.

Joe Z.
fonte
Pode ser uma função? Ou tem que tomar STDIN?
Brigand
2
Você tem a contagem de sílabas disponível para as 3 passagens de exemplo ou apenas o índice? Se você tiver, a contagem de sílabas seria útil para comparação.
Strigoides
Pode ser uma função. De fato, deve ser uma função.
Joe Z.

Respostas:

6

Perl 120 bytes

#!perl -pa0
s@\w+|([.!?])@$s+=$#-,lc($&)=~s![aeiou]+\B|([aeiouy]$)!$y+=1-$#-/3!ger@ge}
{$_=206.835-1.015*@F/$s-84.6*$y/@F

E / S de amostra:

$ perl flesch-kincaid.pl < input1.dat
110.730040322581

$ perl flesch-kincaid.pl < input2.dat
65.6097727272728

$ perl flesch-kincaid.pl < input2.dat
1.71366197183096

A contagem de sílabas é feita assumindo que cada agrupamento de vogais é uma única sílaba, exceto as vogais isoladas no final de uma palavra, que são contadas apenas dois terços do tempo; uma heurística que parece ser bastante precisa.

primo
fonte
3

K & R C - 188 196 199 229 caracteres

Com a especificação alterada para especificar uma função, posso obter grande parte da sobrecarga c da contagem. Também mudando para usar a contagem de sílabas dos Strigoides, que é melhor do que minha fórmula, ajustada e estendida para lidar com a contagem excessiva de palavras.

Depois que encontrei uma maneira mais curta de fazer a detecção de vogais, que infelizmente era baseada stdchr, tive o incentivo de extrair um pouco mais da abominação que eu estava usando para não precisar ser chata.

d,a,v,s,t,w;float R(char*c){for(;*c;++c){s+=*c=='.';if(isalpha(*c)){
w+=!a++;d=(*c&30)>>1;if(*c&1&(d==7|((!(d&1))&(d<6|d>8)))){t+=!v++;}
else v=0;}else v=a=0;}return 206.835-1.*w/s-82.*t/w;}

A lógica aqui é uma máquina de estado simples. Conta frases por períodos apenas, palavras por sequências de caracteres alfabéticos e sílabas como sequências de vogais (incluindo y).

Eu tive que separar um pouco as constantes para que saíssem com os números certos, mas peguei emprestado o truque dos Strigoides de apenas subestimar as sílabas por uma fração fixa.

Sem golfe , com comentários e algumas ferramentas de depuração:

#include <stdlib.h>
#include <stdio.h>
d,a,/*last character was alphabetic */
  v,/*lastcharacter was a vowel */
  s, /* sentences counted by periods */
  t, /* syllables counted by non-consequtive vowels */
  w; /* words counted by non-letters after letters */
float R/*eadability*/(char*c){
  for(;*c;++c){
    s+=*c=='.';
    if(isalpha(*c)){ /* a letter might mark the start of a word or a
               vowel string */
      w+=!a++; /* It is only the start of a word if the last character
              wasn't a letter */
      /* Extract the four bits of the character that matter in determining
       * vowelness because a vowel might mark a syllable */
      d=(*c&30)>>1;
      if( *c&1  & ( d==7 | ( (!(d&1)) & (d<6|d>8) ) ) 
      ) { /* These bits 7 or even and not 6, 8 make for a
         vowel */
    printf("Vowel: '%c' (mangled as %d [0x%x]) counts:%d\n",*c,d,d,!v);
    t+=!v++;
      } else v=0; /* Not a vowel so set the vowel flag to zero */
    }else v=a=0; /* this input not alphabetic, so set both the
            alphabet and vowel flags to zero... */
  }
  printf("Syllables: %3i\n",t);
  printf("Words:     %3i       (t/w) = %f\n",w,(1.0*t/w));
  printf("Sentences: %3i       (w/s) = %f\n",s,(1.0*w/s));
  /* Constants tweaked here due to bad counting behavior ...
   * were:       1.015     84.6 */
  return 206.835-1.   *w/s-82. *t/w;
}
main(c){
  int i=0,n=100;
  char*buf=malloc(n);
  /* Suck in the whole input at once, using a dynamic array for staorage */
  while((c=getc(stdin))!=-1){
    if(i==n-1){ /* Leave room for the termination */
      n*=1.4;
      buf=realloc(buf,n);
      printf("Reallocated to %d\n",n);
    }
    buf[i++]=c;
    printf("%c %c\n",c,buf[i-1]);
  }
  /* Be sure the string is terminated */
  buf[i]=0;
  printf("'%s'\n",buf);
  printf("%f\n",R/*eadability*/(buf));
}

Saída: (usando o andaime da versão longa, mas a função de golfe).

$ gcc readability_golf.c
readability_golf.c:1: warning: data definition has no type or storage class
$ ./a.out < readability1.txt 
'I would not, could not, in the rain.
Not in the dark, not on a train.
Not in a car, not in a tree.
I do not like them, Sam, you see.
Not in a house, not in a box.
Not with a mouse, not with a fox.
I will not eat them here or there.
I do not like them anywhere!
'
104.074631    
$ ./a.out < readability2.txt
'It was a bright cold day in April, and the clocks were striking thirteen.
Winston Smith, his chin nuzzled into his breast in an effort to escape
the vile wind, slipped quickly through the glass doors of Victory Mansions,
though not quickly enough to prevent a swirl of gritty dust from entering
along with him.
'
63.044090
$ ./a.out < readability3.txt 
'When in the Course of human events, it becomes necessary for one people to
dissolve the political bands which have connected them with another, and to
assume among the powers of the earth, the separate and equal station to
which the Laws of Nature and of Nature's God entitle them, a decent respect
to the opinions of mankind requires that they should declare the causes
which impel them to the separation.
'
-1.831667

Deficiências:

  • A lógica da contagem de sentenças está errada, mas eu me livrei disso porque apenas uma das entradas possui a !ou a ?.
  • A lógica de contagem de palavras tratará as contrações como duas palavras.
  • A lógica de contagem de sílabas tratará essas mesmas contrações que uma sílaba. Mas provavelmente superconta em média (por exemplo, thereé contada como duas e muitas palavras terminadas eme serão contadas demais), então apliquei um fator constante de correção de 96,9%.
  • Assume um conjunto de caracteres ASCII.
  • Acredito que a detecção de vogal irá admitir [e {, o que claramente não está certo.
  • Muita confiança na semântica da K&R torna isso feio, mas ei, é código de golfe.

Coisas para olhar:

  • Estou (momentaneamente) à frente da solução python aqui, mesmo se estiver rastreando o perl.

  • Veja a coisa horrível que fiz para detectar vogais. Faz algum sentido se você escrever as representações ASCII em binário e ler o comentário na versão longa.

dmckee --- gatinho ex-moderador
fonte
"Eu tive que alterar a fórmula um pouco à mão para obter resultados aceitáveis". Esta pode ser uma forma incorreta.
Joe Z.
1
Agora, pelo menos, segui o exemplo de Strigoides e fiz os ajustes com base em quem a compreensão do texto comete erros, em vez de um ajuste puramente ad hoc para concordar com os três casos de teste.
dmckee --- ex-moderador gatinho
2

Python, 202 194 188 184 171 171 167 caracteres

import re
def R(i):r=re.split;w=len(r(r'[ \n]',i));s=r('\\.',i);y=r('[^aeiou](?i)+',i);return 206.835-1.015*w/(len(s)-s.count('\n'))-84.6*(len(y)-y.count(' ')-2)*.98/w

Primeiro, obtenha o número total de palavras dividindo-se em espaços e novas linhas:

w=len(r(r'[ \n]',i))

Então, a fórmula. As contagens de sentenças e sílabas são usadas apenas uma vez e, portanto, são incorporadas nessa expressão.

As frases são simplesmente a entrada dividida ., com as novas linhas filtradas:

s=r('\\.',i);s=len(s)-s.count('\n')

As sílabas consistem na divisão da entrada ao longo de não vogais, com espaços removidos. Isso parece superestimar consistentemente o número de sílabas, por isso precisamos ajustá-lo (cerca de 0,98 parece fazê-lo):

y=r('[^aeiou](?i)+',i);y=len(y)-y.count(' ')-2;

202 -> 194: em len(x)-2 vez de len(x[1:-1]). Removidos suportes desnecessários. Regex de sílaba diferencia maiúsculas de minúsculas

194 -> 188: O arquivo foi salvo anteriormente como DOS em vez de formato de arquivo unix, causandowc -c com que as novas linhas sejam contadas como dois caracteres. Ops.

188 -> 184: Livre-se dessas coisas desagradáveis x for x in ... if x!=...armazenando o resultado intermediário e subtraindox.count(...)

184 -> 171: Remova a entrada / saída e converta para a função

171 -> 167: insira len(x)-x.count(...)s na fórmula

Strigoides
fonte
Sua resposta não precisa incluir os procedimentos de entrada e saída.
Joe Z.
@JoeZeng Oh, tudo bem. Vou transformá-lo em uma função então.
Strigoides
1

Python 380 caracteres

import re
def t(p):
 q=lambda e: e!=''
 w=filter(q,re.split('[ ,\n\t]',p))
 s=filter(q,re.split('[.?!]',p))
 c=len(w)*1.0
 f=c/len(s)
 return w,f,c
def s(w):
 c= len(re.findall(r'([aeiouyAEIOUY]+)',w))
 v='aeiouAEIOU'
 if len(w)>2 and w[-1]=='e'and w[-2]not in v and w[-3]in v:c-= 1
 return c
def f(p):
 w,f,c=t(p)
 i=0
 for o in w:
  i+=s(o)
 x=i/c
 return 206.835-1.015*f-84.6*x

Essa é uma solução bastante longa, mas funciona bem o suficiente, pelo menos dos três casos de teste, desde que funcione.

Código de teste

def test():
 test_cases=[['I would not, could not, in the rain.\
        Not in the dark, not on a train.\
        Not in a car, not in a tree.\
        I do not like them, Sam, you see.\
        Not in a house, not in a box.\
        Not with a mouse, not with a fox.\
        I will not eat them here or there.\
        I do not like them anywhere!', 111.38, 103.38, 119.38],\
        ['It was a bright cold day in April, and the clocks were striking thirteen.\
        Winston Smith, his chin nuzzled into his breast in an effort to escape\
        the vile wind, slipped quickly through the glass doors of Victory Mansions,\
        though not quickly enough to prevent a swirl of gritty dust from entering\
        along with him.', 65.09, 57.09, 73.09],\
        ["When in the Course of human events, it becomes necessary for one people to\
        dissolve the political bands which have connected them with another, and to\
        assume among the powers of the earth, the separate and equal station to\
        which the Laws of Nature and of Nature's God entitle them, a decent respect\
        to the opinions of mankind requires that they should declare the causes\
        which impel them to the separation.", 3.70, -4.70, 11.70]]
 for case in test_cases:
  fre= f(case[0])
  print fre, case[1], (fre>=case[2] and fre<=case[3])

if __name__=='__main__':
 test()

Resultado -

elssar@elssar-laptop:~/code$ python ./golf/readibility.py
108.910685484 111.38 True
63.5588636364 65.09 True
-1.06661971831 3.7 True

Eu usei o contador de sílabas daqui - Contando sílabas

Uma versão mais legível está disponível aqui

elssar
fonte
1
if len(w)>2 and w[-1]=='e'and w[-2]not in v and w[-3]in v:c-= 1Mente simples, mas uma boa aproximação. Eu gosto disso.
dmckee --- ex-moderador gatinho
0

Javascript, 191 bytes

t=prompt(q=[]);s=((t[m="match"](/[!?.]+/g)||q)[l="length"]||1);y=(t[m](/[aeiouy]+/g)||q)[l]-(t[m](/[^aeiou][aeiou][s\s,'.?!]/g)||q)[l]*.33;w=(t.split(/\s+/g))[l];alert(204-1.015*w/s-84.5*y/w)

O primeiro caso de teste fornece 112,9 (a resposta correta é 111,4, 1,5 pontos a menos)

O segundo caso de teste fornece 67,4 (a resposta correta é 65,1, menos 2,3 pontos)

O terceiro caso de teste fornece 1,7 (a resposta correta é 3,7 e 2,0 pontos)

SuperJedi224
fonte