Abbiamo già visto come scrivere delle funzioni per creare le mutazioni di una stringa usando ler regex di perl6, ma abbiamo anche detto che questo non funzionerà nel pugs attuale.

Quindi vediamo un’implementazione alternativa degli stessi metodi, basata su Str.substr. L’unica cosa da tenere a mente è che questo metodo fa side effect modificando la stringa corrente, e restituendo la sottostringa eliminata.

IMO sarebbe stato meglio se il metodo restituisse una coppia di valori ($modificata,$eliminata) ma non si è voluta rompere la compatibilità con perl5.

Ad ogni modo, l’assegnazione tra stringhe effettua una copia, quindi per evitare di fare side effect useremo semplicemente una copia della stringa. Il codice dovrebbe essere autoesplicativo:

# 'abc' -> 'ac'
sub deletion($word) {
  (^$word.chars).map: {substr(my $tmp = $word,$_,1)=''; $tmp};
}
	
# 'abc' -> 'adc'
sub substitution($word) {
  gather {
    for (0..$word.chars-1) X @ALPHA {
      substr(my $tmp = $word,$_[0],1)=$_[1];
      take $tmp;
    }
  }
}
	
# 'abc' -> 'abbc'
sub insertion($word) {
  gather {
    for (0..$word.chars) X @ALPHA {
      substr(my $tmp = $word,$_[0],0)=$_[1];
      take $tmp;
     }
  }
}
	
# 'abc' -> 'acb'
sub transposition($w) {
  gather for ^$w.chars {
    my $tmp=$w;
    my $removed =(substr($tmp,$_,1)='');
    substr($tmp,$_+1,0)=$removed;
    take $tmp;
  }
}

Ricordate che il valore restituito da una funzione è l’ultimo calcolato, come in ruby e lisp vari, quindi implicitamente noi restituiamo il valore di gather

È ora possibile definire una funzione edits1 che restituisca tutte le possibili variazioni di una singola lettera per una parola, in questo modo::

sub edits1($w) {
  transposition($w),insertion($w),substitution($w),deletion($w)
}

il comma operator cioè la virgola, concatenate gli array e quindi la funzione restituisce semplicemente un grosso array con tutte le variazioni.

Paragonando questo codice a quello di norvig noterete che lui usa i Set in edits1 mentre noi usiamo semplici liste, ma va bene perché non ci sono duplicati tra le variazioni. Federico credo faccia la stessa cosa in erlang :)

Ora serve una funzione known(@array) che selezioni gli elementi della lista che sono parole valide. Ricordate che abbiamo una variabile %WORDS{word}=>number che abbiamo inizializzato con le parole del vocabolario e la loro frequenza.

La funzione known usando questo dizionario diviene molto semplice:


sub known(@words) {
@words.grep: { %NWORDS{$_}
}

E correct($word) diventa:

sub correct($word) {
  my @values =  known([$word]) or             # the word is known
                              known(edits1($word)) or # a single letter variation is known
                              [$word];                                # we don't have suggestions
	
  # single argument max() doesn't work yet
  @values.max: {%NWORDS{$^a} < => %NWORDS{$^b}}
}

L’ultima linea potrebbe essere più carina ma il codice è comunque abbastanza semplice e pulito.

Per gli ultimi pezzetti aspettate un po’ o implementateli da soli :)