- install docker
- install vscode
- open several terminals
Главная моя идея продемонстрировать похожесть языков друг на друга и то, что технология развивается эволюционно и современные технологии, до сих пор несут на себе отпечаток решений принятых десятилетия, если не столетия, назад.
Шутка про задницу коня и шаттл
Языки делятся на высокоуровневые и низкоуровневые, современные и старые.
График языков
К сожалению, в новых языках сильно ограничена возможность работы с низкоуровневым/опасным? кодом который требует квалификации и понимания принципов работы процессора и памяти.
Высокоуровневые языки скрывают эти механизмы за слоями абстракций и поэтому работа может казаться магией. Я намеренно добавил с++ чтобы продемонстрировать низкоуровневую работу, которая недоступна многим языкам и то как происходит переход к высокоуровневым абстракциям.
Для начала нам необходимо понять окружение работающей программы.
В первом приближении при запусуке любой программы создается три потока данные: стандартный поток ввода, стандартный поток вывода, стандартный поток ошибок. В программу можно передать аргументы, а после завершения работы программа вернет код ошибки: 0 если завершилась штатно или любое другое значение в диапазоне 1-255 если возникла какая-то ошибка. Работающей программе можно послать числовой сигнал от 0 до 64 и программа может прерваться и отреагировать на него, впрочем не на все сигналы https://man7.org/linux/man-pages/man7/signal.7.html. Не все программы используют все из перечисленного. Эта философия пришла из UNIX систем, где каждая команда задумывалась как кирпичики лего, которые можно комбинировать друг с другом. Естественно, не все кирпичики подходили друг к другу, но в простейших случаях, когда поток был текстовым комбинировать можно было достаточно произвольно.
Картинка телетайпа Раньше компьютеры были в виде мейнфреймов – больших залов заставленных стойками с оборудованием, а подключались к ним с телетайпов: печатных машинок совмещенных с принтерами. Сотрудник печатал команду, она передавалась по линиям связи, мейнфрейм выполнял команду и передавал ответ, который печатался на принтере. Затем принтер заменился на экран, появились персональные компьютеры, а концепция терминала, куда печатаешь команду и получаешь ответ так и работает спустя 50 лет.
Это называется REPL (Read Eval Print Loop): подобие диалога, где вы печатаете команду, подтверждаете её выполнение кнопкой Enter. Команда выполняется. Печатается результат выполнения команды. И терминал предлагает ввести следующую команду.
Выполняемые команды называются CLI (Command Line Interface), в отличие от GUI (Graphical User Interface) где человек взаимодейтвует при помощи мыши, нажимая на различные графические элементы.
Вы видите строку приглашение
root@850958de4b2e:~#
Это означает что ползователь root, находится на машине под названием 850958de4b2e, в данный момент находится в каталоге ~ (домашний каталог пользователя) и является привилегированным пользователем #.
Чтобы выйти из терминала напечатайте команду и нажмите enter:
exit
Также выйти можно нажав сочетание клавиш Ctrl+D.
Начнем с простой команды, которая получает параметры и просто выводит их в стандартный поток вывода и нажмите enter чтобы подтвердить ввод:
echo привет мир
После каждой команды результат выполнения записывается в переменную $?
. Вывести её значение можно при помощи команды echo:
echo $?
Результат будет 0, потому что команда завершилась успешно. Обратите внимание, в этой переменной находится результат выполнения последней команды и выполнив echo мы заменили результат выполнения команды echo привер мир на результат выполнения команды echo $?.
По-умолчанию параметры разделяются пробелом. Обратите внимание что несколько пробелов подряд схлопывается при выводе
echo 1 2
Что если для нас эти пробелы важны и мы хотим их вывести, для этого нужно взять строку в одинарные кавычки:
echo '1 2'
Обратите внимание что '1 2' воспринимается как один параметр. Мы можем передать еще параметры
echo '1 2' 3 4
С точки зрения команды echo она получила 3 параметра.
Мы можем передать многострочный текст, bash поймет что команда не закончена и не будет выполнять команду пока вы не закроетк кавычку:
echo '1
> 2
> 3
> 4'
Обратите внимание как строка приглашения сменилась на > – это означает что команда не завершена.
Если вы ошиблись и хотите отменить ввод команды нажмите Ctrl+C. Также это стандартное сочетание клавиш которое может прервать большинство выполняющихся команд.
Мы говорили что команды могут собираться в цепочки, когда вывод одной команды передается на вход другой команды:
Вместо того чтобы писать
root@850958de4b2e:~# echo '1
> 2
> 3
> 4'
Есть стандартная команда которая выводит последовательность чисел:
seq 10
Допустим мы хотим получить строки с 3 по 5. Для этого есть две команды: head -n X, где X – число строк которые мы хотим получить с начала стандартного ввода. И tail -n Y где Y – число строк которые мы хотим получить с конца потока, отбросив все строки кроме последних Y.
Здесь строка, имеется ввиду набор символов оканчивающийся переводом строки (аналог клавиши Enter). Также обратите внимание что -n и X с точки зрения командной строки это два параметра, и это ответственность команды интерпретировать эти аргументы так что -n это параметр, а X его значение.
Команды соединяются между собой при помощи символа | (pipe, пайп, труба).
выполните сначала
seq 10 | head -n 5
потом
seq 10 | head -n 5 | tail -n 3
Большинство команд понимают параметр -h или --help. И выводят справку. Попробуйте вызвать справку по параметрам команды tail:
tail -h
Вы увидите сообщение говорящее что параметр -h неизвестен, для получения справки используйте команду --help.
head: invalid option -- 'h'
Try 'head --help' for more information.
Кстати, поскольку команда завершилась с ошибкой, то в переменной хранящей результат последней команды будет значение 1. Напоминаю, что 0 это успешный результат, а все остальные – считаются ошибочными.
echo $?
Выведите справку по команде tail:
tail --help
Обратите внимание, что большинство параметров содержат короткие односимвольные версии вида -n X и длинные версии --lines=X. Длинные версии удобно использовать в скриптах, поскольку многие команды содержат огромное количество параметров и односимвольные версии очень тяжело запомнить.
Для интереса выведите справку по команде grep которая содержит почти 50 параметров, которые можно комбинировать в любом порядке.
Перепишите команду seq 10 | head -n 5 | tail -n 3 с длинными версиями параметров и убедитесь что результат совпадает с предыдущим.
В данный момент на кране много текста, чтобы очистить его можно нажать сочетание Ctrl+L или выполнить команду
clear
ls, cat, pipe to file, file to pipe.
Откроем 5 терминалов и вызовем команды
docker run -it nix python
docker run -it nix irb
docker run -it nix cling
docker run -it nix ki
docker run -it nix node
ki: print("123")
python: print("123")
irb: print("123")
cling:
#include <cstdio>
printf("123")
node: console.log("123")
print
– это функция которая печатает в стандартный поток вывода строку.
строка это массив символов
почти во всех языках строка записывается в виде литерала (не исполняемого кода, которые передаются как есть) заключенного в двойные или одинарные кавычки. Интерпретируемые языки такие как python, ruby, js поддерживают оба вида литералов.
питон добавляет перевод строки
язык состоит из синтаксиса - очень маленького набора правил и стандартной библиотеки.
библиотека – это логически и физически сгруппированный набор кода (классов, методов, функций, процедур) для того чтобы избегать замусоривания и дубликатов. Также используется для ускорения кода: подключается только то что необходимо. Почти всегда логическое отображение задается расположением кода в папках и подпапках.
функция аналогична математической: получает что-то на вход и возвращает результат. Функция может ничего не возвращать, как например принт, в бейсике такое называлось процедурой. В современных языка процедур нет, а вместо них функции которые возвращают специальное пустое значение void или nil.
Модули могут содержать подмодули и обычно разделяются точками. Но могут разделяться через / в с/с++ или :: в руби
Можно видеть что в с функция печати не доступна по-умолчанию и приходится её подключать. При этом в js функции печати нет, но есть модуль console и метод log.
По всем функциям есть документация
функция print в модуле kotlin.io https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/print.html функция print в модуле IO https://www.rubydoc.info/stdlib/core/IO:print функция print без модуля https://docs.python.org/3/library/functions.html#print функция printf из модуля cstdio https://en.cppreference.com/w/cpp/io/c/printf
Выход из компилятора обычно при помощи ctrl+d. Либо
в ki можно послать команду repl :q
в python quit()
в руби quit
в clang .q
в js process.exit()
Продолжаем работу со строками и рассмотрим перевод строки. Раньше каждый символ кодировался одним байтом и было всего 2^8=256 значений. Очевидно их не хватало и поэтому существовали кодировки где первые 128 символов были одинаковые а вторые 128 были специфичны для каждого языка. Самые распростаненные кодировки это latin-1
или win-1251
с кириллицей. Если кодировка была неправильная то мы видели кракозябры вместо текста.
таблица символов
В таблице символов, есть непечатаемые или управляющие символы. Один из них это LF, Line Feed, 0A, 10 который "прокручивает бумагу" на одну строку вверх и CR, Carriage return, 0D, 13 который переводит "печатающую головку" влево. https://en.wikipedia.org/wiki/Newline. Эти символы пришли к нам из эпохи Морзе 1870-го года. В разных системах они используются по-разному: в Windows перевод строки в текстовом файле обозначается СR LF, в Linux системах достаточно только LF. Поэтому если мы откроем текстовый файл созданный в Linux в Блокноте на Windows мы увидем как все строки слиплись в одну – это из-за того, что блокнот переносит строку только если встретт оба символа.