Perl 6 и последовательности Маркова

в 23:08, , рубрики: markov, perl, perl6, rakudo, Программирование

Рассмотрим одну нечисловую последовательность, основанную на использовании цепей Маркова в тексте. Следующий символ последовательности будет случайным образом определяться на основе двух предыдущих. Распределение следует шаблону, содержащемуся в исходном тексте.

use v6;
use List::Utils;
 
my $model-text = $*IN.slurp.lc;
$model-text .=subst(/<[_']>/, "", :global);
$model-text .=subst(/<-alpha>+/, " ", :global);
 
my %next-step;
for sliding-window($model-text.comb, 3) -> $a, $b, $c {
    %next-step{$a ~ $b}{$c}++;
}
 
my $first = $model-text.substr(0, 1);
my $second = $model-text.substr(1, 1);
my @chain := $first, $second, -> $a, $b { %next-step{$a ~ $b}.roll.key } ... *;
say @chain.munch(80);

После инициализации в коде чётко видны три части.

Первая вводит текст модели и избавляется от не алфавитных символов. Строка 4 использует slurp для чтения стандартного ввода ($*IN) в одну строковую переменную, а lc приводит всё в нижний регистр. Первый subst удаляет все подчёркивания и апострофы. Второй заменяет все последовательности не алфавитных символов пробелами.

Вторая часть применяет функцию sliding-window из List::Utils и магию Perl.

$model-text.comb делит текст на символы.

sliding-window, скользящее окно, проходит по списку и выдаёт N (в данном случае, 3) элемента, начиная с каждого из элементов списка. То есть, сначала вы получаете 1-й, 2-й и 3-й, затем 2-й, 3-й и 4-й, и т.д.

В цикле мы создаём таблицу таблиц. Ключи внешней – это первые два из трёх последовательных символов. Внутренней – третий символ, а его значение – сколько раз этот символ следует за первыми двумя. То есть, скормив программе текст альбома группы Aqualung, мы получим содержимое %next-step{«qu»} вида:

{"a" => 5, "e" => 2}

Это получится, если у нас за «q» и «u» идёт «a» пять раз, а потом «e» два раза.

Третья часть кода использует эти данные для построения последовательности. Мы берём первые два символа, и мы знаем, какой символ идёт за ними. Потом мы создаём последовательность, начинающуюся с этих двух символов, и использующую в качестве генератора -> $a, $b { %next-step{$a ~ $b}.roll }. Он использует предыдущие два символа как хэш частоты для третьего. Метод roll возвращает один случайный ключ хэша, в соответствии с его весом. В примере с «qu» можно представить, что мы кидаем семигранный кубик, у которого 5 граней – «a» и две — «e». Если неизвестно, какой символ идёт за первыми двумя (например, эти два символа были уникальны для текста), то возвращается неопределённое значение, и последовательность останавливается.
Мы получаем первые 80 символов последовательности через метод munch.

Запустив скрипт на текстах Aqualung, мы получаем последовательности вроде
“t carealven thead you he sing i withe and upon a put saves pinsest to laboonfeet” и “t steall gets sill a creat ren ther he crokin whymn the gook sh an arlieves grac”.

В программе не задан жёстко набор символов, с которым она должна работать. Всё, что Perl 6 распознает как символ, будет обработано. Скормив стандартный файл “Land der Berge”, который p6eval использует как stdin, вы получите строки вроде “laß in ber bist brüften las schören zeites öst froher land der äckerzeichöne lan”.

Автор: SLY_G

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js