Dvorak-fr : disposition de clavier ergonomique
Ergonomie du poste de travail informatique
Comments in English below
Tables des fréquences de caractère, digramme et trigramme dans un texte en langue française, établies à l'aide de ce programme
(in-package :CL-user)
;;;; Le calcul de la fréquence d'occurrence des caractères, digrammes et
;;;; trigrammes dans un texte est réalisé par les deux fonctions
;;;; principales suivantes:
;;;;
;;;; 1. la fonction Compter-Caractères fait le décompte des caractères,
;;;; digrammes et trigrammes contenus dans un fichier dont le chemin d'accès
;;;; lui est donné en argument. Les décomptes sont stockés dans des tables de
;;;; hachage (tables associatives).
;;;;
;;;; 2. A partir des données du nombre d'occurrences stockées dans les tables,
;;;; la fonction Rapport-Des-Fréquences publie un rapport de fréquence dont le
;;;; contenu dépend de la valeur de ses arguments (clefs).
;;;; Par défaut le rapport est présenté dans la fenêtre d'interaction et:
;;;; - tous les types d'items sont publiés (caractères, digrammes et
;;;; trigrammes)
;;;; - les items sont triés selon leur fréquence
;;;; - le détail des voyelles accentuées apparaît
;;;; - les espaces, les chiffres et les retours à la ligne sont ignorés
;;;; - les digrammes et les trigrammes dont la fréquence est plus petite que
;;;; 0.2% ne sont pas présentés.
;;;;
;;;; Voir la fin de ce fichier pour des exemples d'utilisation
;;;;
;;;; REMARQUE
;;;; Ce programme est une adaptation française du programme original:
;;;; - la plupart des noms de fonctions et de variables ont été traduits en
;;;; français, pour le reste voir le glossaire ci-dessous
;;;; - quelques commentaires ont été ajoutés pour le lecteur non averti.
;;;; L'objectif premier étant d'assurer un support à un cours d'introduction
;;;; à la programmation (en français).
;;;;
;;;; Glossaire anglais-français
;;;; --------------------------
;;;; char : abréviation de character (caractère)
;;;; count : (dé)compte, compter
;;;; inc : abréviation de increment (incrémenter, incrément)
;;;; input : entrée
;;;; output : sortie
;;;; result : résultat
;;;; string : chaîne de caractères
;;;; tab : caractère tabulation
;;;;
;;;;
;;;; ENGLISH
;;;; *******
;;;;
;;;; Functions to compute and report frequencies of characters, digrams and
;;;; trigrams from a text file
;;;;
;;;; There are 2 main functions:
;;;;
;;;; 1. Compter-Caractères (count-characters) : counts the occurrences of
;;;; characters, digrams and trigrams from a text file. The data are stored in
;;;; hashtables.
;;;; 2. Rapport-Des-Fréquences (report-frequencies) : from the data in the
;;;; hashtables, it reports the frequencies according to its arguments. Unless
;;;; specified otherwise, it:
;;;; - reports frequencies of characters, digrams and trigrams
;;;; - sorts items by frequency
;;;; - distinguishes accented vowels
;;;; - ignores spaces, digits and newlines
;;;; - doesn't publish items whose frequency is less than .2%
;;;;
;;;; Examples:
;;;;
;;;; counts the occurences in text.txt file
;;;; (compter-caractères #p"c:\\test.txt")
;;;;
;;;; reports the frequencies in the toploop window
;;;; (rapport-des-fréquences)
;;;;
;;;; doesn't report digrams and trigrams
;;;; sorts characters alphabetically
;;;; (rapport-des-fréquences :trier-freq? NIL :2g? NIL :3g? NIL)
;;;;
;;;; idem but doesn't distinguish accented vowels
;;;; (rapport-des-fréquences :trier-freq? NIL :accentuées? NIL :2g? NIL :3g? NIL)
;;;;
;;;; reports only digrams with 2 vowels
;;;;(rapport-des-fréquences
;;;; :caract? NIL
;;;; :3g? NIL
;;;; :filtre-grammes (lambda (s)
;;;; (every #'voyelle s)))
;;;;
;;;; reports only digrams with one E at least (accented or not)
;;;;(rapport-des-fréquences
;;;; :caract? NIL
;;;; :3g? NIL
;;;; :filtre-grammes (lambda (s)
;;;; (find #\E s ; items stored in uppercase
;;;; ;; voyelle returns the vowel without accent or NIL
;;;; :key (lambda (c) (or (voyelle c)
;;;; c))))
;;;;
;;;;
;;;; Francis Leboutte - Juin 2002
;;;; f.leboutte @ algo.be
;;;;
;;;****************************************************************************
;;; Variables
;;; 3 tables de hachage pour stocker le décompte caractères, des digrammes
;;; et des trigrammes.
;;; Note: il n'est pas nécessaire d'initialiser le nombre d'entrées d'une
;;; table de hachage.
;;;
;;; entrée : la clef d'accès est le caractère (majuscule), la valeur est le
;;; nombre d'occurences
(defvar *caractères* (make-hash-table))
;;; entrée : la clef d'accès est le digramme (string, majuscule), la valeur est
;;; le nombre d'occurences
(defvar *digrammes* (make-hash-table :test #'equal))
;;; entrée : la clef d'accès est le digramme (string, majuscule), la valeur est
;;; le nombre d'occurences
(defvar *trigrammes* (make-hash-table :test #'equal))
;;; pour conserver le chemin d'accès du Fichier à Analyser
(defvar *chemin-FA* NIL)
;;;****************************************************************************
;;; La fonction compter-caractères fait le décompte des caractères, des
;;; digrammes et des trigrammes contenus dans un fichier dont le chemin d'accès
;;; lui est donné en argument. Les décomptes sont stockés dans des tables de
;;; hachage.
;;; Chemin : chemin d'accès du fichier à analyser (pathname)
;;; NB des espaces (#\space) consécutifs sont comptés pour un seul espace
(defun compter-caractères (chemin
&optional
;; par défaut les tabs (caractère tabulation) ne
;; sont pas comptés
(tab? NIL))
;; remise à zéro des tables
(clrhash *caractères*)
(clrhash *digrammes*)
(clrhash *trigrammes*)
(unless (and (or (stringp chemin) (pathnamep chemin))
(probe-file chemin))
(error "Il n'existe pas de fichier correspondant au chemin d'accès~%~A"
chemin))
(setf *chemin-FA* chemin)
(let (;; pour le décompte des espaces consécutifs (pour information)
(espaces-non-comptés 0))
;; ouverture d'un flot (Stream) sur le fichier (Path). Note : le flot est
;; fermé automatiquement dès la sortie de la forme (with-open-file …)
(with-open-file (stream chemin :direction :input)
(loop ; boucle
with caract-précédent and c1 and c2 and c3
;; lecture d'un caractère - eof : End Of File
as char = (read-char stream NIL :eof)
while (not (eq char :eof))
do
(cond ((char= char #\space)
(setf c1 NIL c2 NIL c3 NIL)
(if (char= #\space caract-précédent)
(incf espaces-non-comptés)
(inc-char-count #\space)))
((char= #\tab char)
(setf c1 NIL c2 NIL c3 NIL)
(when tab?
(inc-char-count #\tab)))
((char= #\newline char)
(setf c1 NIL c2 NIL c3 NIL)
(inc-char-count #\newline))
(T
(inc-char-count char)
(setf c3 char)
(when c2
(inc-grammes c1 c2 c3))
(setf c1 c2 c2 c3)))
(setf caract-précédent char)))
(format t "~% espaces non comptés : ~D" espaces-non-comptés)
))
;;; A partir des données du nombre d'occurrences stockées dans les tables,
;;; la fonction Rapport-Des-Fréquences publie un rapport de fréquence dont le
;;; contenu dépend de la valeur des arguments (clefs).
;;; Par défaut le rapport est présenté dans la fenêtre d'interaction et:
;;; - tous les types d'items sont publiés (caractères, digrammes et trigrammes)
;;; - les items sont triés selon leur fréquence
;;; - le détail des voyelles accentuées apparaît
;;; - les espaces, les chiffres et les retours à la ligne sont ignorés
;;; - les digrammes et les trigrammes dont la fréquence est plus petite que 0.2%
;;; ne sont pas présentés
(defun rapport-des-fréquences (&key
;; où sortir le rapport :
;; - soit dans la fenêtre d'interaction (T), le défaut
;; - soit dans un fichier (pathname designator)
(chemin T)
;; T : tri selon la fréquence, sinon tri alphabétique
(trier-freq? T)
;; T : fait le détail des voyelles accentuées
(accentuées? T)
;; NIL : les espaces et tabs n'interviennent pas
;; dans le décompte
(espaces? NIL)
;; NIL : les chiffres n'interviennent pas
(chiffres? NIL)
;; NIL : les retours à la ligne n'interviennent pas
(retours? NIL)
;; % , seuil de fréq à partir duquel un n-gramme est
;; présenté dans le rapport
(seuil-de-fréq .2)
(nbre-décimales 2)
;; présente les caractères ou non
(caract? T)
;; présente les digrammes ou non
(2g? T)
;; présente les trigrammes ou non
(3g? T)
;; fonction d'un argument (un di ou un trigramme) pour
;; ne publier que certains des digrammes (trigrammes)
;; Voir les exemples d'utilisation (fin du fichier)
filtre-grammes)
;; définition de 3 fonctions locales
(labels ((intervient? (caract)
(and (or chiffres?
(not (digit-char-p caract)))
(or espaces?
(not (member caract '(#\tab #\space))))
(or retours?
(not (char= caract #\newline)))))
(format-ligne (stream caract count total)
(format stream "~&~A~C~A~C~,vF"
(nom-caract caract)
#\tab count #\tab
nbre-décimales
(float (* 100 (/ count total)))))
(rapport-des-fréquences-aux (stream)
(let ((total 0)
(result nil))
(format stream "~&Analyse du fichier ~A~2%" *chemin-FA*)
(when caract?
(maphash (lambda (k v) ; Key (caract) - Value (décompte)
(when (intervient? k)
(let (voyelle)
(cond (accentuées?
(push (cons k v) result))
((setf voyelle (voyelle k))
(let ((dp (assoc voyelle result)))
(if dp
(incf (cdr dp) v)
(push (cons voyelle v) result))))
(T (push (cons k v) result))))
(incf total v)))
*caractères*)
(format stream "~&~D occurences de caractères:~2%" total)
(setf result
(if trier-freq?
(sort result #'>= :key #'cdr)
(sort result
;; tri alphabétique: il ne suffit pas d'utiliser le
;; prédicat char< , car il faut que les voyelles accentuées
;; tombent en bonne place …
(lambda (caract-majuscule1 caract-majuscule2)
(let ((voy1 (voyelle caract-majuscule1))
(voy2 (voyelle caract-majuscule2)))
(if (and voy1 voy2 (char= voy1 voy2))
(char< caract-majuscule1 caract-majuscule2)
(char< (if voy1 voy1 caract-majuscule1)
(if voy2 voy2 caract-majuscule2)))))
:key #'car)))
(mapc (lambda (dp)
(let ((caract (car dp))
(count (cdr dp)))
(when (intervient? caract)
(format-ligne stream caract count total))))
result))
(rapport-des-di-et-trigrammes stream
:seuil-de-fréq seuil-de-fréq
:nbre-décimales nbre-décimales
:filtre-grammes filtre-grammes
:2g? 2g?
:3g? 3g?))))
;; corps principal
(if (eq t chemin)
(rapport-des-fréquences-aux T)
(with-open-file (stream chemin :direction :output
:if-does-not-exist :create
:if-exists :overwrite)
(rapport-des-fréquences-aux stream)))
(values)))
(defun rapport-des-di-et-trigrammes (stream
&key
(seuil-de-fréq .2)
(nbre-décimales 2)
filtre-grammes
(2g? T)
(3g? T))
(flet ((rapport-table (table-h)
(let ((total-occurrences 0)
(nbre-n-grammes 0) ; nbre-n-grammes différents
(result NIL)
(gram-length (block :n (maphash (lambda (k v)
(declare (ignore v))
(return-from :n (length k)))
table-h))))
(maphash (lambda (k v)
(incf nbre-n-grammes)
(push (cons k v) result)
(incf total-occurrences v))
table-h)
(setf result (sort result #'>= :key #'cdr))
(format stream "~2%~D ~A différents (~D occurences):~2%"
nbre-n-grammes
(if (= gram-length 2) "digrammes" "trigrammes")
total-occurrences)
(block :report
(mapc (lambda (dp)
(let* ((k (car dp))
(count (cdr dp))
(percent (float (* 100 (/ count total-occurrences)))))
(cond ((< percent seuil-de-fréq)
(return-from :report))
((or (not filtre-grammes)
(funcall filtre-grammes k))
(format stream "~&~A~C~A~C~,vF"
k #\tab count #\tab
nbre-décimales percent))
(T NIL))))
result)))))
(when 2g? (rapport-table *digrammes*))
(when 3g? (rapport-table *trigrammes*))
(values)))
;;; incrémente les décomptes dans les tables *digrammes* et *trigrammes*
;;; selon les arguments
(defun inc-grammes (c1 c2 c3)
(declare (optimize (speed 3) (safety 1) (debug 0) (space 0)))
;; définition d'une macro locale
(macrolet ((char-up (char) ; caractère en majuscule
`(when (alpha-char-p ,char)
(setf ,char (char-upcase ,char)))))
;; définition d'une fonction locale
(flet ((inc-grammes-aux (n-gramme table-h)
(let ((value (gethash n-gramme table-h)))
(cond (value
(setf (gethash n-gramme table-h) (1+ value)))
;; le di(tri)gramme doit être copié car la fonction
;; make-string+ ne crée pas de nouvelle chaîne de
;; caractères
(T (setf (gethash (copy-seq n-gramme) table-h) 1))))))
(char-up c2)
(char-up c3)
(inc-grammes-aux (make-string+ 2 c2 c3) *digrammes*)
(when c1
(char-up c1)
(inc-grammes-aux (make-string+ 3 c1 c2 c3) *trigrammes*))
)))
;;; Retourne une chaîne de caractères de longueur Size contenant les
;;; caractères Chars (qui doivent être au nombre de Size)
;;; NB ne crée pas de nouvelle chaîne de caractères
(defun make-string+ (size &rest chars)
;; declaration pour le compilateur (optimisation de la vitesse d'exécution)
(declare (optimize (speed 3) (safety 1) (debug 0) (space 0)))
;; load-time-value : optimisation, S2 (S3) est associés à un objet littéral
;; (literal object) dans le code compilé (une chaîne de caractères de
;; longueur 2 - string). On évite ainsi la reconstruction du string à chaque
;; appel de la fonction make-string+
(let* ((s2 (load-time-value (make-string 2)))
(s3 (load-time-value (make-string 3)))
(s (if (= size 2) s2 s3)))
(loop for char in chars
as i = 0 then (1+ i)
do
(setf (char s i) char))
s))
(defun inc-char-count (char)
(when (alpha-char-p char)
(setf char (char-upcase char)))
(let ((value (gethash char *caractères*)))
(cond (value
(setf (gethash char *caractères*) (1+ value)))
(T (setf (gethash char *caractères*) 1)))))
;;; si c'est une voyelle accentuée retourne la même voyelle sans accent (majuscule)
;;; sinon si c'est une voyelle sans accent retourne la voyelle (majuscule)
;;; sinon (ce n'est pas une voyelle) retourne NIL
;;; Function pouvant servir au français et au néerlandais au moins
(defun voyelle (caract-majuscule)
(case caract-majuscule
((#\E #\É #\È #\Ê #\Ë) #\E)
((#\I #\Î #\Ï) #\I)
((#\U #\Ù #\Û #\Ü) #\U)
((#\A #\À #\Â #\Ä) #\A)
((#\O #\Ô #\Ö) #\O)
(T NIL)))
(defun nom-caract (caract)
(case caract
(#\tab "tab")
(#\space "espa")
(#\. "pt")
(#\, "virg")
(#\; "pt-v")
(#\' "apos")
(#\" "guil")
(#\newline "ret")
(#\` "bkq") ; Accent grave isolé ou 'backquote' - pas en français
(T (if (alphanumericp caract)
caract
(let ((nom (char-name caract)))
(if nom
nom
caract))))))
;;;****************************************************************************
#|
Exemples d'utilisation
----------------------
;;; analyse (comptage) du fichier c:\mes documents\test.txt
(compter-caractères #p"c:\\mes documents\\test.txt")
;;; publication des résultats dans la fenêtre d'interaction
(rapport-des-fréquences)
;;; publication dans le fichier c:\mes documents\rapport-test.txt
(rapport-des-fréquences :chemin #p"c:\\mes documents\\rapport-test.txt")
;;; publie uniquement les caractères (pas les digrammes, ni les trigrammes),
;;; triés alphabétiquement
(rapport-des-fréquences :trier-freq? NIL :2g? NIL :3g? NIL)
;;; même chose mais une voyelle accentuée est comptée sous la voyelle
;;; sans l'accent
(rapport-des-fréquences :trier-freq? NIL :accentuées? NIL :2g? NIL :3g? NIL)
;;; publie uniquement les digrammes à 2 voyelles
(rapport-des-fréquences
:caract? NIL
:3g? NIL
:filtre-grammes (lambda (s)
(every #'voyelle s)))
;;; publie uniquement les digrammes comportant au moins un E (accentué ou non)
(rapport-des-fréquences
:caract? NIL
:3g? NIL
:filtre-grammes (lambda (s)
(find #\E s ; #\E et pas #\e car stockage majuscule
;; voyelle retourne la voyelle sans accent ou NIL
:key (lambda (c) (or (voyelle c)
c)))))
|#