9  Как распространять работу?

Существует достаточно много причин, по которой необходимо уметь распространять свою работу:

9.1 Cимволы подстановки (wildcards)

Как и во многих языках программирования командная строка поддерживает символы подстановки. Хоть я приведу все примеры с программой ls, легко представить себе и более осмысленные задачи. Для примера, представим, что у нас есть папка с большим количеством файлов:

```{shell}
$ ls multiple_files
```
a_1.csv
a_1.txt
b_2_2.csv
c_1.txt
d_2.txt
ee_1.txt
f_2.txt
g_1.txt
hh_2.txt
i_1.txt
j_2.txt
k_1.txt
ll_2.txt
m_1.txt
n_2.csv
oo_1.csv
pp_2.csv
q_1.txt
r_2.txt
s_1.txt
t_2.txt
u_1.txt
v_2.txt
w_1.txt
x_2.csv
y_1.txt
z_2.txt

Мы можем вывести все файлы содержащие двойку, добавив * – любое количество символов:

```{shell}
$ ls multiple_files/*2*
```
examples/09_multiple_files/b_2_2.csv
examples/09_multiple_files/d_2.txt
examples/09_multiple_files/f_2.txt
examples/09_multiple_files/hh_2.txt
examples/09_multiple_files/j_2.txt
examples/09_multiple_files/ll_2.txt
examples/09_multiple_files/n_2.csv
examples/09_multiple_files/pp_2.csv
examples/09_multiple_files/r_2.txt
examples/09_multiple_files/t_2.txt
examples/09_multiple_files/v_2.txt
examples/09_multiple_files/x_2.csv
examples/09_multiple_files/z_2.txt

Это же используют для поиска файлов с каким-либо определенным расширением:

```{shell}
$ ls multiple_files/*.csv
```
examples/09_multiple_files/a_1.csv
examples/09_multiple_files/b_2_2.csv
examples/09_multiple_files/n_2.csv
examples/09_multiple_files/oo_1.csv
examples/09_multiple_files/pp_2.csv
examples/09_multiple_files/x_2.csv

Кроме того звездочку можно использовать и без каких либо окружающих символов, тогда будут перечислены все файлы. Например, следующий команда удаляет все файлы в папке multiple_files.

```{shell}
$ rm multiple_files/*
```

Если известно точное количество символов, то можно использовать символ ?, который обозначает один символ. Выведем только файлы с четырьмя символами до расширения:

```{shell}
$ ls multiple_files/????.*
```
examples/09_multiple_files/ee_1.txt
examples/09_multiple_files/hh_2.txt
examples/09_multiple_files/ll_2.txt
examples/09_multiple_files/oo_1.csv
examples/09_multiple_files/pp_2.csv

Искать можно и по расширению:

```{shell}
$ ls multiple_files/a_1.???
```
examples/09_multiple_files/a_1.csv
examples/09_multiple_files/a_1.txt

Кроме того можно задавать группы при помощи квадратных скобок:

```{shell}
$ ls multiple_files/[aouie]*
```
examples/09_multiple_files/a_1.csv
examples/09_multiple_files/a_1.txt
examples/09_multiple_files/ee_1.txt
examples/09_multiple_files/i_1.txt
examples/09_multiple_files/oo_1.csv
examples/09_multiple_files/u_1.txt

9.2 Введение в git

источник: https://xkcd.com/1597/

9.2.1 .gitignore

Символы подстановки очень полезны при создании файла .gitignore. .gitignore — это специальный скрытый файл1, в котором вы можете перечислить файлы, которые программа git должна игнорировать (см. документацию).

При создании морфологического анализатора достаточно обмениваться файлами .lexd, .twol, .cg3 и инструкциями по компиляции анализатора, которые обычно записывают в Makefile (см. раздел Раздел 9.3). Получающиеся файлы вроде .hfst или .hfstol хотелось бы не закоммитить случайно созданона GitHub. В связи с этим в вашем репозитории имеет смысл перечислить файлы, которые следует игнорировать версии контроля. Вот как будет выглядеть такой файл .gitignore:

*.hfst
*.hfstol

Позже может так случится, что вы будете хранить в той же папке литературу и/или данные, которые нельзя выкладывать, их тоже можно будет добавить в файл .gitignore. Кроме того у GitHub есть ограничения на размер файла (вроде 100мб?..), если он увидит большие файлы, то он не даст их добавить, так что если они есть в вашем проекте, их тоже следует добавить в .gitignore.

9.2.2 git hooks

Кажется удобным, чтобы проверки, которые мы обсудили в Раздел 8.3, можно было проводить каждый раз перед тем как делать коммит. Для этого были придуманы git hooks (подробнее можно почитать здесь). Если Вы написали скрипт для проверки, то его можно записать в файл .git/hooks/pre-commit, тогда каждый раз он будет запускаться. Можно даже сделать так, чтобы коммит не отправлялся, если тест не проходит.

9.3 Программа make

В ходе курса мы видели, что для того, чтобы скомпилировать морфологический анализатор и генератор достаточно достаточно маленьких текстовых файлов и целая вереница команд в командной строке. Понятно, что достаточно бессмысленно пробовать держать в памяти нужный порядок команд. Для этого придумали (в 1976 году) программу, которая называется make, которая последовательно запускает череду инструкций, которые записаны в виде терминальных команд в файле Makefile (см. огромную документацию). В результате, в идеальном сценарии, пользователь скачивает папку, набирает в терминале make и получает рабочий анализатор.

Makefile представляет собой собрание рецептов следующей структуры:

```{Makefile}
target: dependency1 dependency2 ...
[Tab ↹]<script>
```

Обратите внимание, что перед скриптом стоит один таб (ваш текстовый редактор может заменить его на пробелы).

Рассмотрим следующий игрушечный трансдьюсер:

```{shell}
$ cat example.lexd
```
PATTERNS
Noun [<N>:] Suffix[-adj] | (Suffix[adj] AdjInflection)
Noun [<N>:] NounInflection

LEXICON Noun
ночь
печь

LEXICON Suffix
<dim>:ка
<adj>:н[adj]

LEXICON AdjInflection
<m><sg><nom>:ой

LEXICON NounInflection
<nom>:
<acc>:
```{shell}
$ cat example.twol
```
Alphabet
  а е й к н о п ч ь ь:0;

Rules

"чк чн пишется без ь"
! например, ночьной -> ночной или печька -> печка

ь:0 <=> _ к;
        _ н;
```{shell}
$ cat ru_en.lexd
```
PATTERNS
translations

LEXICON translations
печь:stove
ночь:night
<N>
<acc>
<adj>
<dim>
<m>
<nom>
<sg>

Создадим следующий файл Makefile:

```{shell}
$ cat Makefile
```
analyzer_english_stem.hfst: analyzer.hfst ru_en.lexd
    lexd ru_en.lexd | hfst-txt2fst | hfst-repeat -f 1 -o ru_en.hfst
    hfst-compose analyzer.hfst ru_en.hfst -o analyzer_english_stem.hfst
    rm ru_en.hfst

analyzer.hfst: generator.hfst
    hfst-invert generator.hfst -o analyzer.hfst

generator.hfst: example.lexd twol.hfst
    hfst-twolc -q example.twol -o twol.hfst
    lexd example.lexd | hfst-txt2fst | hfst-compose-intersect twol.hfst -o generator.hfst
    rm twol.hfst

clean: 
    rm -f *.hfst

Запустим наш Makefile:

```{shell}
$ make
```
hfst-twolc -q example.twol -o twol.hfst
lexd example.lexd | hfst-txt2fst | hfst-compose-intersect twol.hfst -o generator.hfst
rm twol.hfst
hfst-invert generator.hfst -o analyzer.hfst
lexd ru_en.lexd | hfst-txt2fst | hfst-repeat -f 1 -o ru_en.hfst
hfst-compose analyzer.hfst ru_en.hfst -o analyzer_english_stem.hfst
rm ru_en.hfst

Применим получившийся трансдьюсер:

```{shell}
$ echo "ночка" | hfst-lookup analyzer_english_stem.hfst
```
ночка   night<N><dim>   0,000000

Теперь мы научились хранить инструкции по компиляции в Makefile.

В рецептах можно использовать переменные:

  • $@ (целевой объект),
  • $^ (список зависимостей),
  • $< (первая из зависимостей).

Тогда наш Makefile можно переписать следующим образом:

```{shell}
$ cat Makefile
```
analyzer_english_stem.hfst: analyzer.hfst ru_en.lexd
    lexd ru_en.lexd | hfst-txt2fst | hfst-repeat -f 1 -o ru_en.hfst
    hfst-compose $< ru_en.hfst -o $@
    rm ru_en.hfst

analyzer.hfst: generator.hfst
    hfst-invert $< -o $@

generator.hfst: example.lexd example.twol
    hfst-twolc -q example.twol -o twol.hfst
    lexd $< | hfst-txt2fst | hfst-compose-intersect twol.hfst -o $@
    rm twol.hfst

clean: 
    rm -f *.hfst

Ради интереса можно посмотреть на Makefile, который мы использовали в начале занятий:

.DEFAULT_GOAL: requirements

.PHONY: requirements forms clean

requirements:
    @curl -s https://apertium.projectjj.com/apt/install-nightly.sh | sudo bash
    sudo apt-get install hfst lexd

task.generator.hfst: task.lexd
    @lexd task.lexd | hfst-txt2fst -o lexd.hfst
    @if [ ! -f task.twol ]; then\
        mv lexd.hfst $@;\
    else\
        hfst-twolc -q task.twol -o twol.hfst;\
        hfst-compose-intersect lexd.hfst twol.hfst -o $@;\
    fi

task.analyzer.hfst: task.generator.hfst
    @hfst-invert $^ -o $@

analysis: task.analyzer.hfst
    @echo "$(FORM)" | hfst-lookup $^
    @rm -f *.hfst

generation: task.generator.hfst
    @echo "$(FORM)" | hfst-lookup $^
    @rm -f *.hfst

for_test_%.txt:
    @curl -s https://raw.githubusercontent.com/agricolamz/2025_morphological_transducers/refs/heads/main/task_tests/$@ -o $@

forms: task.generator.hfst
    @hfst-fst2strings $^
    @rm -f *.hfst

clean: 
    @rm -f *.hfst *.txt

test_%: for_test_%.txt task.generator.hfst
    @hfst-fst2strings task.generator.hfst > generated_forms.txt
    @$(eval N_over_wrong=`grep -xvf $< generated_forms.txt | wc -l`)
    @if [ "$(N_over_wrong)" != "0" ]; then\
        echo "Overgeneration or wrong generation:\n" > test_results.txt;\
        grep -xvf $< generated_forms.txt >> test_results.txt;\
    fi
    @$(eval N_not_generated=`grep -xvf generated_forms.txt $<  | wc -l`)
    @if [ "$(N_not_generated)" != "0" ]; then\
        echo "\nNot generated:\n" >> test_results.txt;\
        grep -xvf generated_forms.txt $< >> test_results.txt;\
    fi
    @if [ ! -f test_results.txt ]; then\
        echo "\e[32mAll tests for the task have passed.\e[0m";\
    else\
        echo "\e[31mIt looks like your lexd/twol files failed one of the tests.\n\e[0m";\
        cat test_results.txt;\
    fi
    @rm -f *.hfst *.txt

  1. Скрытые файлы или папки начинаются с точки и не высвечиваются по команде ls (но высвечиваются по команде ls -a). Скрытые файлы часто хранят конфигурационные файлы или какие-то данные, которые используют программы. Эта категория файлов создана, чтобы пользователь случайно не переписал или удалил какой-то из файлов.↩︎