English version
Version française ci-dessous.
I just wrote haspirater, a system to
detect if the initial 'h' in a French word is aspirated or not. (I
happened to need this, and no one had apparently done it yet.) For those
who are unfamiliar with the context, the thing is that for French words
which start with an 'h', the 'h' can be aspirated or non aspirated,
which changes the behavior of the word regarding elision and liaison. Of
course, there are no known rules to find out from the structure of a
word whether it is aspirated or not...
The simple approach would be to use word lists, but of
course they are never complete and will fail for unseen words. A
natural solution for them would be to assume that they behave in the
same way that the closest known word. This forces us to define
"closest", and it seems reasonable to look at the word with the longest
common prefix, because it looks like the property of whether the 'h' is
aspired or not should be mostly conditioned by the beginning of the
word. (Actually, it would be better to look at the
pronunciation of the beginning of the word if we could afford
it).
This suggests a simple optimization. If we are going to take the
result of the closest word in this fashion, we might as well drop words
from the known words list which do not contribute anything to the
result. In other words, if all words starting with "hach" in the list
have an aspirated 'h', then there is no need to store them all; just
storing "hach" will be enough. In fact, this means that the appropriate
structure for our word list is a trie, and the optimization
that I mentioned is apparently called compression.
Another trick is that we can try to infer word lists automatically
from a corpus using some simple rules. If we read "la hache" in a text,
it means that the initial 'h' is aspirated, whereas "l'hirondelle"
indicates that "hirondelle" starts with a non-aspirated 'h'. We can
easily process megabytes of text and get word lists in this way.
This approach works quite well, yielding a dataset which is under
5 KB in json (and less than 1 KB compressed) and a lookup
program which is just 40 lines of Python. It gives the right result for
any example I could come up with (though I had to add a few exceptions
for words missing from the corpus, both manually and using Wikipedia and
Wiktionary); hopefully it will also work for you, please tell me if it
doesn't. Note that we could even compile the trie into a very efficient
C program if speed was of the essence.
Another amusing thing is that we can draw the data and try to see
what the "rules" are. Here is the
trie (it is quite messy). The node border and edge thickness are are
a logarithmic function of the number of occurrences, the node labels
indicate the prefix, and the color is red for aspirated h and blue for
non-aspirated (or a mix thereof for ambiguous cases, depending on the
proportion).
The Wikipedia article I linked above mentions that this approach is
used to store Unicode character properties efficiently; it seems like it
could be used for a lot of other things. For instance, you could imagine
the trie indicating the gender of nouns (though you would probably build
it on suffixes rather than prefixes in this case), the trie of
prepositions used for a given noun (say "в" vs "на" in Russian), and
probably tries for a lot of other arbitrary things that are so obvious
to native speakers.
Version française
English version above.
Je viens d'écrire haspirater, un système pour
détecter les 'h' aspirés au début des mots français. (Il se trouve que
j'en avais besoin, et, apparemment, personne ne l'avait encore fait.)
Pour ceux qui ne connaissent pas le contexte, le problème est que pour
les mots français qui commencent par un 'h', le 'h' peut être aspiré ou
non aspiré (ce qui ne change rien à la prononciation, mais change le
fonctionnement de l'élision et
de la liaison).
Évidemment, il n'y a pas de règles pour déterminer à partir de la
structure d'un mot si le 'h' initial est aspiré ou non...
L'approche la plus simple serait d'utiliser une liste de mots, mais
évidemment de telles listes ne sont jamais complètes et ne
fonctionneront pas pour des mots inconnus. Une solution naturelle pour
ces derniers serait de supposer qu'ils se comportent de la même manière
que le mot connu le plus proche. Cela nous oblige à définir
"proche" : il paraît raisonnable de regarder le mot ayant le plus
long préfixe commun avec le mot inconnu, parce qu'on a tendance à se
dire que c'est le début du mot qui a le plus d'influence sur le fait que
le 'h' soit aspiré ou non. (En fait, ce serait plutôt la
prononciation du début du mot qu'il faudrait regarder si on la
connaissait, mais passons...)
Cela nous mène à une optimisation simple. Si on prend toujours le
résultat du mot le plus proche de la façon décrite plus haut, on peut
très bien retirer de la liste de mots connus ceux qui ne sont pas
nécessaires pour déduire le résultat. En d'autres termes, si tous les
mots qui commencent par "hach" dans la liste ont un 'h' aspiré, alors il
n'est pas nécessaire de les stocker tous, il suffit de stocker "hach".
En fait, cela veut dire que la bonne structure pour notre liste de mots
est un trie, et
l'optimisation que je viens de décrire s'appelle apparemment la compression.
Une autre astuce est qu'on peut essayer d'inférer automatiquement des
listes de mots à partir d'un corpus en utilisant quelques règles
simples. Si on lit "la hache" dans un texte, cela signifie que le 'h'
initial est aspiré, alors que "l'hirondelle" indique qu'"hirondelle"
commence par un 'h' non aspiré. On peut facilement traiter plusieurs
mégaoctets de texte pour en tirer des listes de mots de cette façon.
Cette approche fonctionne plutôt bien : le fichier de données
fait moins de 5 Ko en json (et moins de 1 Ko compressé), et le
programme pour chercher un mot fait 40 lignes de Python. Il donne le bon
résultat pour tous les exemples que j'ai essayés (quoique j'ai dû
ajouter à la main quelques exceptions pour des mots absents du
corpus, en utilisant les listes de Wikipédia et du Wiktionnaire) ;
j'espère qu'il marchera aussi pour vous, et vous prie de me signaler
toute erreur. Notez qu'on pourrait même compiler le trie en un programme
en C très efficace si la vitesse était importante.
Un autre truc amusant qu'on peut faire, c'est faire un dessin des
données et voir quelles semblent être les "règles". Voici le trie
(il est plutôt bordélique). L'épaisseur du bord des nœuds et des arêtes
est en fonction du nombre d'occurrences (logarithmiquement), les
étiquettes des nœuds indiquent le préfixe, les nœuds en rouge indiquent
l'aspiration, les nœuds en bleu indiquent la non-aspiration, et un
mélange indique les cas ambigus (en fonction de la proportion).
L'article Wikipédia cité plus haut indique que cette approche est
utilisée pour un stockage efficace des propriétés des caractères
Unicode ; j'ai l'impression qu'elle pourrait être utilisée pour
tout un tas d'autres choses. On pourrait imaginer, par exemple, un trie
du genre des noms (quoiqu'il serait probablement préférable de regarder
les suffixes plutôt que les préfixes dans ce cas), un trie du choix des
propositions en fonction du nom ("в" vs "на" en russe, entre autres), et
sans doute des tries pour un tas d'autres choses arbitraires qui sont
tellement évidentes pour les locuteurs natifs.