From 4e88b2cdba021d9be72d8e64793d6cd086e8710b Mon Sep 17 00:00:00 2001 From: weirdvic <56339132+weirdvic@users.noreply.github.com> Date: Wed, 12 Apr 2023 19:51:05 +0300 Subject: Added russian translation for Tcl page --- ru-ru/tcl-ru.html.markdown | 589 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 589 insertions(+) create mode 100644 ru-ru/tcl-ru.html.markdown diff --git a/ru-ru/tcl-ru.html.markdown b/ru-ru/tcl-ru.html.markdown new file mode 100644 index 00000000..723efe30 --- /dev/null +++ b/ru-ru/tcl-ru.html.markdown @@ -0,0 +1,589 @@ +--- +language: Tcl +contributors: + - ["Poor Yorick", "https://pooryorick.com/"] +translators: + - ["Viktor Sokhranov", "https://github.com/weirdvic"] +filename: learntcl.tcl +--- + +Tcl был создан [Джоном Оустерхаутом](https://ru.wikipedia.org/wiki/Оустерхаут,_Джон) +в качестве скриптового языка в своих инструментах проектирования электрических цепей. +В 1997 году за разработку языка Tcl автор получил [ACM](https://ru.wikipedia.org/wiki/ACM) + Software System Award. Tcl может использоваться и как встраиваемый скриптовый язык, +и как язык программирования общего назначения. Кроме того, он может быть использован как +библиотека в программах на C, даже в случаях когда не требуется написание скриптов, +поскольку Tcl может предоставить программе на C различные типы данных, такие как +динамические строки, списки и хэш-таблицы. Также с помощью этой библиотеки возможно +использовать форматирование строк, операции с файловой системой, работу с кодировками и +динамически загружаемые библиотеки. К другим особенностям Tcl относятся: + +* Удобный кроссплатформенный API для работы с сетью + +* Поддержка виртуальной файловой системы(VFS) + +* Стекируемые каналы ввода-вывода + +* Асинхронность в ядре языка + +* Поддержка корутин + +* Простая и надёжная модель потоков выполнения + +Tcl имеет много общего с Lisp, но в отличие от списков, в Tcl "валютой" языка +являются строки. Все значения являются строками. Список в Tcl это просто строка в +определённом формате, а тело процедуры(скрипт) это ещё одна строка, а не блок. +С целью увеличения производительности, интерпретатор Tcl использует кэшированные +внутренние представления различных типов данных. Например, рутины(routines), работающие +со списками, фактически используют внутреннее представление списков, а интерпретатор +Tcl обновляет строковое представление в том случае если оно используется в скрипте. +В Tcl используется подход copy-on-write, позволяющий оперировать большими объёмами +данных без дополнительного оверхеда. Процедуры в Tcl автоматически компилируются +в байткод, кроме случаев когда в процедуре используются динамические рутины, такие +как "uplevel", "upvar" и "trace" + +Программировать на Tcl приятно. Его находят привлекательным хакеры, которым интересны +Lisp, Forth или Smalltalk, а также инженеры и учёные, которым просто необходим +гибкий инструмент для выполнения их задач. В Tcl языковые конструкции, включая +циклы и математические операторы, представлены в виде изменяемых рутин, в отличие +от других языков программирования, где они закреплены в синтаксисе, что позволяет +синтаксису Tcl не мешать работать с предметной областью проекта. Синтаксис Tcl в этом +смысле даже более минималистичен чем у Lisp. + + + +```tcl +#! /bin/env tclsh + +############################################################################### +## 1. Рекомендации +############################################################################### + +# Tcl это не shell или C! Этот момент требует уточнения, поскольку привычки +# написания shell-скриптов почти работают в Tcl и часто люди начинают +# изучать Tcl со знанием синтаксиса других языков. Поначалу это работает, но +# когда скрипты становятся сложнее, наступает фрустрация. + +# Фигурные скобки {} в Tcl используются не для построения блоков кода или +# списков, а как механизм экранирования(quoting) для кода. Фактически в Tcl +# нет ни списков, ни блоков кода. Фигурные скобки использутся для +# экранирования специальных символов и потому подходят для представления +# тела процедур и строк, которые должны интерпретироваться как списки. + + +############################################################################### +## 2. Синтаксис +############################################################################### + +# Скрипт состоит из команд, разделённых символами перевода строки или символами +# точки с запятой. Каждая команда представляет собой вызов рутины. Первое слово +# это имя вызываемой рутины, а последующие слова это аргументы. Слова разделены +# пробелами. Так как каждый аргумент это слово в команде, он является строкой и +# может быть неэкранирован: +set part1 Sal +set part2 ut; set part3 ations + + +# символ доллара используется для подставления значения переменных: +set greeting $part1$part2$part3 + + +# Когда "set" получает только имя переменной, возвращается значение переменной: +set part3 ;# Возвращает значение переменной + + +# Содержимое квадратных скобок заменяется на результат выполнения: +set greeting $part1$part2[set part3] + + +# Встроенный таким образов скрипт может состоять из нескольких команд, но +# результат подстановки определяется последней командой: +set greeting $greeting[ + incr i + incr i + incr i +] +puts $greeting ;# Выведет "Salutations3" + +# Каждое слово в команде является строкой, включая имя рутины, поэтому +# подстановки могут быть использованы и таким образом: +set action pu + +# следующие команды эквивалентны: +puts $greeting +${action}ts $greeting +[set action]ts $greeting + + +# Обратный слэш экранирует специальные символы: +set amount \$16.42 + + +# и он же используется для ввода специальных символов: +puts lots\nof\n\n\n\n\n\nnewlines + + +# Слово в фигурных скобках никак не интерпретируется и в нём не работают +# никакие подстановки, за исключением экранирования закрывающей скобки: +set somevar { + Это литерал знака $, а это \} экранированная закрывающая скобка +} + + +# В слове внутри двойных кавычек, пробельные символы теряют своё +# специальное значение: +set name Neo +set greeting "Hello, $name" + + +# Имя переменной может быть любой строкой: +set {first name} New + + +# Фигурные скобки используются для доступа к переменным с составными именами: +set greeting "Hello, ${first name}" + + +# "set" всегда можно использовать вместо подстановки переменной: +set greeting "Hello, [set {first name}]" + + +# Чтобы "распаковать" список в команду используется оператор расширения "{*}" +# Эти две команды эквивалентны: +set name Neo +set {*}{name Neo} + + +# Массив это особая переменная, являющаяся контейнером для других переменных. +set person(name) Neo +set person(destiny) {The One} +set greeting "Hello, $person(name)" + + +# "variable" может быть использована для объявления или установки переменных. +# В отличие от "set", которая использует глобальное и локальное пространство +# имён, "variable" работает только с локальным пространством: +variable name New + + +# "namespace eval" создаёт новое пространство имён, если его не существует. +# Пространство имён может содержать рутины и переменные: +namespace eval people { + namespace eval person1 { + variable name Neo + } +} + + +# Двумя или более двоеточиями в именах переменных отделяется название +# пространства имён: +namespace eval people { + set greeting "Hello $person1::name" +} + +# Два или более двоеточия также отделяют название пространства имён +# в имени рутины: +proc people::person1::speak {} { + puts {I am The One.} +} + +# Полные(fully-qualified) имена начинаются с двух двоеточий: +set greeting "Hello $::people::person1::name" + + + +############################################################################### +## 3. Больше никакого синтаксиса +############################################################################### + +# Все остальные функции реализованы посредством рутин. С этого момента и далее +# больше нет нового синтаксиса. Всё остальное что можно изучить о Tcl это +# поведение отдельных рутин и какие значения они присваивают своим аргументам. + + + +############################################################################### +## 4. Переменные и пространства имён +############################################################################### + +# Каждая переменная и рутина связанс с пространством имён. + +# Чтобы получить интерпретатор, которые не может сделать ничего, достаточно +# удалить глобальное пространство имён. Особой пользы в этом нет, но это хорошо +# иллюстрирует природу Tcl. Фактически имя глобального пространства имён это +# пустая строка, но единственный способ представить её -- в виде полного имени: +proc delete_global_namespace {} { + namespace delete :: +} + +# Поскольку "set" всегда учитывает и глобальное, и текущее пространства имён, +# более безопасно использовать "variable" чтобы объявить новую переменную или +# задать значение переменной. Если переменная с именем "name" уже существует +# в глобальном пространстве имён, использование "set" задаст значение +# глобальной переменной, тогда как "variable" работает только с текущим +# пространством имён. + +namespace eval people { + namespace eval person1 { + variable name Neo + } +} + +# После объявления переменной в пространстве имён, [set] видит её, а не +# одноимённую переменную в глобальном пространстве имён: + +namespace eval people { + namespace eval person1 { + variable name + set name Neo + } +} + +# Но если "set" приходится создать новую переменную, он всегда делает это +# с учётом текущего пространства имён: +unset name +namespace eval people { + namespace eval person1 { + set name neo + } + +} +set people::person1::name + + +# Абсолютное имя всегда начинается с имени глобального пространства имён, то +# есть с пустой строки, за которой следует два двоеточия: +set ::people::person1::name Neo + + +# В пределах процедуры "variable" связывает перменную в текущем пространстве +# имён с локальной областью видимости: +namespace eval people::person1 { + proc fly {} { + variable name + puts "$name is flying!" + } +} + + + + +############################################################################### +## 5. Встроенные рутины +############################################################################### + +# Математические операции можно выполнять при помощи "expr": +set a 3 +set b 4 +set c [expr {$a + $b}] + +# Поскольку "expr" самостоятельно занимается подстановкой значений переменных, +# математическое выражение нужно оборачивать в фигурные скобки чтобы отключить +# подстановку значений переменных интерпретатором Tcl. +# Подробнее об этом можно прочесть здесь: +# "https://wiki.tcl-lang.org/page/Brace+your+expr-essions" + + +# "expr" выполняет подстановку переменных и результатов команд: +set c [expr {$a + [set b]}] + + +# "expr" предоставляет разные математические функции: +set c [expr {pow($a,$b)}] + + +# Математические операторы сами по себе доступны в виде рутин в +# пространстве имён ::tcl::mathop +::tcl::mathop::+ 5 3 + +# Рутины могут быть импортированы из других пространств имён: +namespace import ::tcl::mathop::+ +set result [+ 5 3] + + +# Не числовые значения должны быть квотированы. Такие операторы как "eq" +# Могут быть использованы чтобы провести строковое сравнение: +set name Neo +expr {{Bob} eq $name} + +# Общие операторы сравнения тоже работают со строками если числовое значение +# операнда недоступно: +expr {{Bob} == $name} + + +# "proc" создаёт новые рутины: +proc greet name { + return "Hello, $name!" +} + +# можно указать несколько параметров: +proc greet {greeting name} { + return "$greeting, $name!" +} + + +# Как было отмечено ранее, фигурные скобки не обозначают блок кода. +# Любое значение, даже третий аргумент "proc" является строкой. +# Предыдущая команда может быть переписана без использования фигурных скобок: + +# As noted earlier, braces do not construct a code block. Every value, even +# the third argument to "proc", is a string. The previous command +# can be rewritten using no braces: +proc greet greeting\ name return\ \"\$greeting,\ \$name!\" + + + +# Если последний параметр называется "args", все дополнительные аргументы, +# переданные рутине, собираются в список и передаются как "args": +proc fold {cmd first args} { + foreach arg $args { + set first [$cmd $first $arg] + } + return $first +} +fold ::tcl::mathop::* 5 3 3 ;# -> 45 + + +# Условное выполнение тоже реализовано как рутина: +if {3 > 4} { + puts {This will never happen} +} elseif {4 > 4} { + puts {This will also never happen} +} else { + puts {This will always happen} +} + + +# Циклы реализованы как рутины. Первый и третий аргументы для "for" +# обрабатываются как скрипты, а второй аргумент как выражение: +set res 0 +for {set i 0} {$i < 10} {incr i} { + set res [expr {$res + $i}] +} +unset res + + +# Первый аргумент для "while" тоже обрабатывается как выражение: +set i 0 +while {$i < 10} { + incr i 2 +} + + +# Список это строка, а элементы списка разделены пробелами: +set amounts 10\ 33\ 18 +set amount [lindex $amounts 1] + +# Если элемент списка содержит пробел, его надо экранировать: +set inventory {"item 1" item\ 2 {item 3}} + + +# Хорошая практика использовать списковые рутины для обработки списков: +lappend inventory {item 1} {item 2} {item 3} + + +# Фигурные скобки и бэкслеш могут быть использованы чтобы хранить более +# комплексные структуры внутри списков. Список выглядит как скрипт, за +# исключением того, что перевод строки и точка с запятой теряют своё +# специальное значение, а также не производится подстановка значений. +# Эта особенность Tcl называется гомоиконичность +# https://ru.wikipedia.org/wiki/Гомоиконичность +# В приведённом списке есть три элемента: +set values { + + one\ two + + {three four} + + five\{six + +} + + +# Поскольку как и все значения, список является строкой, строковые +# операции могут выполняться и над списком, с риском повреждения: +set values {one two three four} +set values [string map {two \{} $values] ;# $values больше не \ + правильно отформатированный список + + +# Безопасный способ работать со списками — использовать "list" рутины: +set values [list one \{ three four] +lappend values { } ;# добавить символ пробела как элемент в список + + +# Использование "eval" для вычисления значения скрипта: +eval { + set name Neo + set greeting "Hello, $name" +} + + +# Список всегда можно передать в "eval" как скрипт, содержащий одну команду: +eval {set name Neo} +eval [list set greeting "Hello, $name"] + + +# Следовательно, когда используется "eval", используйте "list" чтобы собрать +# необходимую команду: +set command {set name} +lappend command {Archibald Sorbisol} +eval $command + + +# Частая ошибка: не использовать списковые функции для построения команды: +set command {set name} +append command { Archibald Sorbisol} +try { + eval $command ;# Здесь будет ошибка, превышено количество аргументов \ + к "set" в {set name Archibald Sorbisol} +} on error {result eoptions} { + puts [list {received an error} $result] +} + +# Эта ошибка запросто может произойти с "subst": + +set replacement {Archibald Sorbisol} +set command {set name $replacement} +set command [subst $command] +try { + eval $command ;# Та же ошибка, лишние аргументы к \ + {set name Archibald Sorbisol} +} trap {TCL WRONGARGS} {result options} { + puts [list {received another error} $result] +} + + +# "list" корректно форматирует значение для подстановки: +set replacement [list {Archibald Sorbisol}] +set command {set name $replacement} +set command [subst $command] +eval $command + + +# "list" обычно используется для форматирования значений для подстановки в +# скрипты, вот несколько примеров: + + +# "apply" вычисляет список из двух элементов как рутину: +set cmd {{greeting name} { + return "$greeting, $name!" +}} +apply $cmd Whaddup Neo + +# Третий элемент может быть использован для указания пространства имён рутины: +set cmd [list {greeting name} { + return "$greeting, $name!" +} [namespace current]] +apply $cmd Whaddup Neo + + +# "uplevel" вычисляет скрипт на уровень выше в списке вызовов: +proc greet {} { + uplevel {puts "$greeting, $name"} +} + +proc set_double {varname value} { + if {[string is double $value]} { + uplevel [list variable $varname $value] + } else { + error [list {not a double} $value] + } +} + + +# "upvar" связывает переменную на текущем уровне вызовов с переменной на +# более высоком уровне: +proc set_double {varname value} { + if {[string is double $value]} { + upvar 1 $varname var + set var $value + } else { + error [list {not a double} $value] + } +} + + +# Избавляемся от встроенной рутины "while" и используем "proc" чтобы написать +# свою версию: +rename ::while {} +# обработка оставлена как упражнение: +proc while {condition script} { + if {[uplevel 1 [list expr $condition]]} { + uplevel 1 $script + tailcall [namespace which while] $condition $script + } +} + + +# "coroutine" создаёт новый стек вызовов, новую рутину для входа в этот стек +# и вызывает эту рутину. "yield" приостанавливает вычисления в этом стеке и +# возвращает управление вызывавшему стеку: +proc countdown count { + # отправить что-нибудь обратно создателю корутины, фактически + # останавливая стек вызовов на время. + yield [info coroutine] + + while {$count > 1} { + yield [incr count -1] + } + return 0 +} +coroutine countdown1 countdown 3 +coroutine countdown2 countdown 5 +puts [countdown1] ;# -> 2 +puts [countdown2] ;# -> 4 +puts [countdown1] ;# -> 1 +puts [countdown1] ;# -> 0 +catch { + puts [coundown1] ;# -> invalid command name "countdown1" +} cres copts +puts $cres +puts [countdown2] ;# -> 3 + + +# Стеки корутин могут передавать контроль друг другу: + +proc pass {whom args} { + return [yieldto $whom {*}$args] +} + +coroutine a apply {{} { + yield + set result [pass b {please pass the salt}] + puts [list got the $result] + set result [pass b {please pass the pepper}] + puts [list got the $result] +}} + +coroutine b apply {{} { + set request [yield] + while 1 { + set response [pass c $request] + puts [list [info coroutine] is now yielding] + set request [pass a $response] + } +}} + +coroutine c apply {{} { + set request [yield] + while 1 { + if {[string match *salt* $request]} { + set request [pass b salt] + } else { + set request [pass b huh?] + } + } +}} + + + +``` + +## Ссылки + +[Официальная документация Tcl](https://www.tcl-lang.org) + +[Tcl Wiki](https://wiki.tcl-lang.org) + +[Tcl на Reddit](http://www.reddit.com/r/Tcl) -- cgit v1.2.3