Construire un indice de lisibilité

13

L' algorithme de lisibilité Flesch-Kincaid dépend des mesures du nombre de mots et du nombre de syllabes, qui ne sont ni entièrement objectives, ni facilement automatisables à l'aide d'un ordinateur. Par exemple, "code-golf", avec le tiret, compte-t-il pour un ou deux mots? Le mot «million» est-il composé de deux ou trois syllabes? Dans cette tâche, vous devrez approximer, car compter exactement prendra trop de temps, d'espace et, surtout, de code.

Votre tâche est de créer le plus petit programme possible (c'est-à-dire une fonction) dans n'importe quelle langue qui prendra un passage de lecture en anglais (supposé être en phrases complètes), et de calculer l'indice Flesch Reading Ease à une tolérance de huit points (pour tenir compte de variations dans le comptage des syllabes et des mots). Il est calculé comme suit:

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

Votre programme doit être aligné sur les passages de référence ci-dessous, dont les indices ont été calculés à l'aide d'un comptage manuel:

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!

Index: 111.38 (64 syllabes en 62 mots en 8 phrases)

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.

Index: 65.09 (74 syllabes en 55 mots en 2 phrases)

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.

Index: 3.70 (110 syllabes en 71 mots en 1 phrase)

Si vous avez d'autres passages pour lesquels vous avez compté manuellement les syllabes et les mots et calculé l'index, vous pouvez les montrer comme vérification.

Joe Z.
la source
Peut-il s'agir d'une fonction? Ou faut-il prendre STDIN?
Brigand
2
Avez-vous le nombre de syllabes disponible pour les 3 exemples de passages, ou juste l'index? Si vous l'avez, le nombre de syllabes serait utile pour la comparaison.
Strigoides
Cela peut être une fonction. En fait, ce devrait être une fonction.
Joe Z.

Réponses:

6

Perl 120 octets

#!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

Exemple d'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

Le comptage des syllabes se fait en supposant que chaque groupe de voyelles est une syllabe unique, à l'exception des voyelles isolées à la fin d'un mot, qui ne sont comptées que les deux tiers du temps; une heuristique qui semble assez précise.

primo
la source
3

K&R c - 188 196 199 199 229 caractères

Avec la spécification modifiée pour spécifier une fonction, je peux obtenir une grande partie de la surcharge c du décompte. Changement également pour utiliser le hack de comptage de syllabes de Strigoides qui est meilleur que mon ajustement de formule et étendu pour faire face au comptage excessif des mots.

Après avoir trouvé un moyen plus court de faire la détection des voyelles, qui était malheureusement basé sur stdchr, j'ai été incité à en extraire un peu plus de l'abomination bidirectionnelle que j'utilisais pour ne pas avoir à être ennuyeux.

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 logique ici est une simple machine à états. Il compte les phrases par périodes seulement, les mots par chaînes de caractères alphabétiques et les syllabes comme chaînes de voyelles (y compris y).

J'ai dû traîner un peu les constantes pour obtenir les bonnes figures, mais j'ai emprunté le tour de Strigoides consistant à sous-estimer les syllabes par une fraction fixe.

Non golfé , avec des commentaires et quelques outils de débogage:

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

Sortie: (en utilisant l'échafaudage de la version longue, mais la fonction golfée.)

$ 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

Lacunes:

  • La logique de comptage des phrases est erronée, mais je m'en sors car une seule des entrées a un !ou un ?.
  • La logique de comptage de mots traitera les contractions comme deux mots.
  • La logique de comptage des syllabes traitera ces mêmes contractions comme une seule syllabe. Mais probablement surévalue en moyenne (par exemple, thereest compté comme deux et de nombreux mots se terminant par eseront comptés un de trop), j'ai donc appliqué un facteur constant de correction de 96,9%.
  • Suppose un jeu de caractères ASCII.
  • Je crois que la détection des voyelles admettra [et {, ce qui n'est clairement pas correct.
  • Beaucoup de dépendance à la sémantique K&R rend cela laid, mais bon, c'est du golf de code.

Choses à regarder:

  • Je suis (momentanément) en avance sur les deux solutions python ici, même si je suis à la traîne de Perl.

  • Obtenez une charge de la chose horrible que j'ai faite pour détecter les voyelles. Cela a du sens si vous écrivez les représentations ASCII en binaire et lisez le commentaire dans la version longue.

dmckee --- chaton ex-modérateur
la source
"J'ai dû modifier la formule un peu à la main pour obtenir des résultats acceptables." Cela peut être une mauvaise forme.
Joe Z.
1
J'ai maintenant au moins suivi l'exemple de Strigoides et fait les ajustements en fonction de qui la compréhension du texte fait des erreurs plutôt qu'un ajustement purement ad hoc pour sonner les trois cas de test en accord.
dmckee --- chaton ex-modérateur
2

Python, 202 194 188 184 171 171 167 caractères

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

Tout d'abord, obtenez le nombre total de mots en divisant le long des espaces et des retours à la ligne:

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

Ensuite, la formule. Les décomptes de phrases et de syllabes ne sont utilisés qu'une seule fois, ils sont donc intégrés dans cette expression.

Les phrases sont simplement l'entrée divisée ., avec des retours à la ligne filtrés:

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

Les syllabes consistent en une entrée divisée le long de non-voyelles, avec des espaces supprimés. Cela semble constamment légèrement surestimer le nombre de syllabes, nous devons donc l'ajuster (environ 0,98 semble le faire):

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

202 -> 194: len(x)-2 plutôt que len(x[1:-1]). Suppression des supports inutiles. Rendu syllabe insensible à la casse

194 -> 188: Le fichier était précédemment enregistré au format DOS plutôt qu'au format de fichier Unix, ce wc -cqui fait compter les sauts de ligne comme deux caractères. Oups.

188 -> 184: Débarrassez-vous de ces méchants x for x in ... if x!=...en stockant le résultat intermédiaire et en soustrayantx.count(...)

184 -> 171: Supprimer l'entrée / sortie et convertir en fonction

171 -> 167: insérer le len(x)-x.count(...)s dans la formule

Strigoides
la source
Votre réponse ne doit pas inclure les procédures d'entrée et de sortie.
Joe
@JoeZeng Oh, d'accord. Je vais ensuite en faire une fonction.
Strigoides
1

Python 380 caractères

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

C'est une solution assez longue, mais elle fonctionne assez bien, au moins des 3 cas de test à condition qu'elle le fasse.

Code de test

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

Résultat -

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

J'ai utilisé le compteur de syllabes d'ici - Compter les syllabes

Une version plus lisible est disponible ici

Elsar
la source
1
if len(w)>2 and w[-1]=='e'and w[-2]not in v and w[-3]in v:c-= 1Simple d'esprit mais une bonne approximation. Je l'aime.
dmckee --- chaton ex-modérateur
0

Javascript, 191 octets

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)

Le premier cas de test donne 112,9 (la bonne réponse est 111,4, en baisse de 1,5 point)

Le deuxième test élémentaire donne 67,4 (la bonne réponse est 65,1, en retrait de 2,3 points)

Le troisième cas de test donne 1,7 (la bonne réponse est 3,7, diminuée de 2,0 points)

SuperJedi224
la source