Crear un índice de legibilidad

13

El algoritmo de legibilidad de Flesch-Kincaid depende de las medidas del recuento de palabras y el recuento de sílabas, ninguno de los cuales es completamente objetivo o fácilmente automatizable utilizando una computadora. Por ejemplo, ¿"code-golf", con el guión, cuenta como una o dos palabras? ¿Es la palabra "millón" dos o tres sílabas? En esta tarea, deberá aproximarse, ya que contar exactamente tomará demasiado tiempo, espacio y, lo más importante, código.

Su tarea es construir el programa más pequeño posible (es decir, una función) en cualquier idioma que tome un pasaje de lectura en inglés (se supone que está en oraciones completas), y calcular el índice Flesch Reading Ease con una tolerancia de ocho puntos (para tener en cuenta variaciones en el conteo de sílabas y el conteo de palabras). Se calcula de la siguiente manera:

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

Su programa debe estar alineado con los pasajes de referencia a continuación, cuyos índices se calcularon utilizando el conteo 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 en 62 palabras en 8 oraciones)

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 en 55 palabras en 2 oraciones)

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 en 71 palabras en 1 oración)

Si tiene otros pasajes para los cuales ha contado manualmente las sílabas y palabras y ha calculado el índice, puede mostrarlas como verificación.

Joe Z.
fuente
¿Puede ser una función? ¿O tiene que tomar STDIN?
Brigante
2
¿Tiene el recuento de sílabas disponible para los 3 pasajes de ejemplo, o solo el índice? Si lo tiene, el conteo de sílabas sería útil para la comparación.
Strigoides
Puede ser una función. De hecho, debería ser una función.
Joe Z.

Respuestas:

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

Muestra de E / S:

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

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

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

El recuento de sílabas se realiza suponiendo que cada grupo de vocales es una sola sílaba, excepto las vocales solitarias al final de una palabra, que solo se cuentan dos tercios del tiempo; Una heurística que parece ser bastante precisa.

primo
fuente
3

K & R C - 188 196 199 229 caracteres

Con la especificación modificada para especificar una función, puedo obtener gran parte de la sobrecarga de la cuenta. También cambio para usar el hack de conteo de sílabas de Strigoides, que es mejor que mi ajuste de fórmula y se extendió para tratar el conteo excesivo de palabras.

Después de encontrar una forma más corta de hacer la detección de vocales, que lamentablemente se basaba en ello stdchr, tuve el incentivo de exprimir un poco más de la abominación poco tonta que estaba usando para no tener que ser aburrida.

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;}

La lógica aquí es una máquina de estado simple. Cuenta las oraciones solo por puntos, las palabras por cadenas de caracteres alfabéticos y las sílabas como cadenas de vocales (incluyendo y).

Tuve que manipular un poco las constantes para que saliera con las cifras correctas, pero he tomado prestado el truco de Strigoides de no contar las sílabas por una fracción fija.

Sin golf , con comentarios y algunas herramientas de depuración:

#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));
}

Salida: (usando el andamio de la versión larga, pero la función de golf).

$ 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

Deficiencias:

  • La lógica de conteo de oraciones es incorrecta, pero me salgo con la suya porque solo una de las entradas tiene a !o a ?.
  • La lógica de conteo de palabras tratará las contracciones como dos palabras.
  • La lógica de conteo de sílabas tratará esas mismas contracciones como una sílaba. Pero probablemente los excesos en promedio (por ejemplo, therese cuentan como dos y muchas palabras que terminan ese contarán como demasiadas), por lo que he aplicado un factor constante de corrección del 96.9%.
  • Asume un conjunto de caracteres ASCII.
  • Creo que la detección de vocales admitirá [y {, lo que claramente no es correcto.
  • Mucha confianza en la semántica de K&R hace que esto sea feo, pero bueno, es el código golf.

Cosas a mirar:

  • Estoy (momentáneamente) por delante de ambas soluciones de Python aquí, incluso si estoy siguiendo el perl.

  • Obtenga una carga de lo horrible que hice para detectar las vocales. Tiene sentido si escribe las representaciones ASCII en binario y lee el comentario en la versión larga.

dmckee --- gatito ex moderador
fuente
"Tuve que modificar la fórmula un poco a mano para obtener resultados aceptables". Esta puede ser una mala forma.
Joe Z.
1
Ahora, al menos, he seguido el ejemplo de Strigoides e hice los ajustes en base a quién la comprensión del texto comete errores en lugar de un ajuste puramente ad hoc para poner de acuerdo los tres casos de prueba.
dmckee --- ex-gatito moderador
2

Python, 202 194 188 184 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

Primero, obtenga el número total de palabras dividiéndolas en espacios y líneas nuevas:

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

Entonces, la fórmula. Los recuentos de oraciones y sílabas solo se usan una vez, por lo que están incrustados en esta expresión.

Las oraciones son simplemente la entrada dividida ., con nuevas líneas filtradas:

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

Las sílabas consisten en la entrada dividida a lo largo de no vocales, con espacios eliminados. Esto parece constantemente sobreestimar ligeramente el número de sílabas, por lo que debemos ajustarlo hacia abajo (aproximadamente .98 parece hacerlo):

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

202 -> 194: en len(x)-2 lugar de len(x[1:-1]). Se eliminaron los corchetes innecesarios. Hecho sílaba regex insensible a mayúsculas y minúsculas

194 -> 188: el archivo se guardó previamente como dos en lugar de formato de archivo unix, lo wc -cque hace que se cuenten nuevas líneas como dos caracteres. Whoops

188 -> 184: Deshágase de esos x for x in ... if x!=...s desagradables almacenando el resultado intermedio y restandox.count(...)

184 -> 171: Eliminar entrada / salida y convertir a función

171 -> 167: inserte la len(x)-x.count(...)s en la fórmula

Strigoides
fuente
Su respuesta no tiene que incluir los procedimientos de entrada y salida.
Joe Z.
@JoeZeng Oh, está bien. Lo convertiré en una función entonces.
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

Esta es una solución bastante larga, pero funciona lo suficientemente bien, al menos de los 3 casos de prueba siempre que lo haga.

Código de prueba

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

Usé el contador de sílabas desde aquí - Contando sílabas

Una versión más legible está disponible aquí

elssar
fuente
1
if len(w)>2 and w[-1]=='e'and w[-2]not in v and w[-3]in v:c-= 1De mente simple pero una buena aproximación. Me gusta.
dmckee --- ex gatito moderador el
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)

El primer caso de prueba da 112.9 (la respuesta correcta es 111.4, apagada por 1.5 puntos)

El segundo caso de prueba da 67.4 (la respuesta correcta es 65.1, apagada en 2.3 puntos)

El tercer caso de prueba da 1.7 (la respuesta correcta es 3.7, apagada por 2.0 puntos)

SuperJedi224
fuente