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
9 Как распространять работу?
Существует достаточно много причин, по которой необходимо уметь распространять свою работу:
- Резервное копирование. Хранить свою работу в одном месте достаточно недальновидно: компьютеры ломаются, ноутбуки крадут, да и люди (а теперь и набирающие популярность LLM-агенты) могут сделать ошибку, которая приведет к потере данных.
- Возможность совместной работы. Разместив проект в некотором общедоступном месте, вы значительно облегчите совместную разработку.
- Возможность получить обратную связь и помощь. Разместив проект в некотором общедоступном месте, вы значительно облегчите другим процесс оценки вашей работы. Не говоря уже о том, что общедоступный проект может привлечь внимание некоторого стороннего разработчика, который захочет как-то улучшить проект.
- История изменений. Все современные системы для работы с кодом поддерживают в том или ином виде историю изменений. Иногда это делают даже облачные системы, которые раз в некоторое время синхронизируются с вашей файловой системой. Это очень полезно, так как позволяет вернутся на несколько шагов назад, если с вашим процессом разработки пошло что-то не так.
- Запуск сервиса. Если хочется сделать сервис, чтобы люди в мире могли пользоваться результатами вашего труда, имеет смысл сделать так, чтобы вашу работу можно было скачать и установить на отдельном сервере (в контейнере и т. п.).
9.1 Cимволы подстановки (wildcards)
Как и во многих языках программирования командная строка поддерживает символы подстановки. Хоть я приведу все примеры с программой ls
, легко представить себе и более осмысленные задачи. Для примера, представим, что у нас есть папка с большим количеством файлов:
```{shell}
$ ls multiple_files
```
Мы можем вывести все файлы содержащие двойку, добавив *
– любое количество символов:
```{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
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
Скрытые файлы или папки начинаются с точки и не высвечиваются по команде
ls
(но высвечиваются по командеls -a
). Скрытые файлы часто хранят конфигурационные файлы или какие-то данные, которые используют программы. Эта категория файлов создана, чтобы пользователь случайно не переписал или удалил какой-то из файлов.↩︎