Пора заменить Python как язык для обучения / Хабр
В последние десять лет мой стандартный ответ на вопрос «с какого языка начинать знакомство с программированием?» был прост — Python. Теперь я меняю свою рекомендацию. Python все еще хороший язык. Он позволяет сфокусироваться на задаче и не волноваться об архитектурных заморочках. О штуках, которые опытные программисты считают важными, позабыв о том, каково это — быть абсолютным новичком. Сам язык растворяется на фоне, и вместо объяснения возможностей и философий, уроки посвящены генерации музыкальных звукорядов, вычислению расстояний на стадионе в зависимости от беговой дорожки, или написанию автоматического игрока в покер или ятцы.
И вот в один прекрасный день студент задаст невинный вопрос: «А как сделать так, чтобы симулятор покера был не в командной строке, а в окне, с кнопкой для выдачи следующих карт?»
Сложно описать сложность этого вопроса. Он заставляет рассматривать различные GUI-инструменты для Python.
Спустя неделю — новый вопрос: «Как написать простую игру, с графикой?»
Пора снова изучить варианты. Pyglet выглядит многообещающе, но он не обновлялся с июля 2012 года. Есть библиотеки, которые фокусируются на чем-то конкретном и не пытаются делать все подряд, например, SplatGL, но она довольно новая и сложно найти достаточное количество примеров. PyGame вроде как популярен, есть даже книга, так что окей, давайте учиться как использовать PyGame.
Спустя месяц новые вопросы: «Как я могу поделиться своей игрой с другом? Хотя… можно ли закачать эту игру на телефон, чтобы показать всем, и чтобы им не нужно было ничего устанавливать?»
Эмм…
Все эти вопросы заставили меня отказаться от Python как языка для обучения. Конечно, всегда будут те, кто считают правильным только олдскульный путь — файлы со скриптами алгоритмов, которые генерируют монохромный текстовый вывод в терминале — но нужно понимать уровень изоляции, который сопровождает такой выбор.
Это зачастую совсем не то, что хотят делать люди. Да, можно найти аддоны практически для всего, но какие из них прошли через пот и ругань серьезных проектов? Какие хорошо поддерживаются сегодня, но будут забыты завтра?Рост популярности не-десктопов все усложняет, да. Я изучал Erlang чтобы уйти от С и С++ и изменить свой уровень мышления. Я доказал, что могу использовать Erlang и чисто функциональный подход в той сфере, которую все больше всего боятся: игры. А потом вышел Айфон и все. Erlang’у больше нет места.
Именно из-за этих мыслей и опыта теперь я рекомендую JavaScript в качестве языка для обучения. Я знаю, знаю, он причудливый и иногда просто странный, но в целом это нормальный и достаточно современный язык. Самое главное, он работает на беспрецедентно повсеместной кросс-платформенной системе для разметки, типографики и рендера. Хочешь показывать элементы интерфейса, изображения или текст? Используй HTML напрямую. Хочешь графику и анимацию? Используй canvas.
Я ожидаю реакции многих на такое изменение мышления: ужас и страх. Эти реакции не должны быть связаны с недостатками JavaScript. Они должны быть связаны с тем, что я проигнорировал кучу других языков, не взирая на их возможности, системы типизации или синтаксис, просто потому что у них нет нативной поддержки в веб-браузерах.
Создайте свой текстовый редактор на Rust! Часть 2 | Кофи Отуо
В первой части этого руководства мы настроили наш проект и создали новый бинарный файл Rust под названием pound. Пока отображается только «Hello world!» в терминале. Давайте теперь попробуем прочитать нажатия клавиш пользователем.
Этот код содержит много информации, поэтому давайте рассмотрим его. Чтобы получить пользовательский ввод, нам нужно ввести в область видимости библиотеку io
(ввод/вывод). Библиотека io
взята из стандартной библиотеки ( станд.
). std::io::Read
— это признак
, который предоставляет метод .read
. Функция stdin
возвращает экземпляр std::io::Stdin
, который является типом, представляющим дескриптор стандартного ввода для вашего терминала. Поскольку мы хотим читать по 1 байту за раз, мы передаем [0;1]
функции read
. Когда read()
достигает конца файла , он возвращает 0.
Чтобы выйти из вышеуказанной программы, нажмите Ctrl-D, чтобы сказать read()
достигнут конец файла. Или вы всегда можете нажать Ctrl-C, чтобы сигнализировать о немедленном завершении процесса.
Нажмите q для выхода ⚒
Давайте добавим новый способ выхода из программы.
Нажмите q для выхода Теперь, чтобы выйти из программы, вам нужно ввести текст с буквой «q» и нажать Введите
. Программа считывала каждую букву, сохраняла ее в переменной buf
и затем проверяла, совпадает ли эта буква с q
. Обратите внимание на [b'q']
. buf
относится к типу [u8;1]
и может сравниваться только с аналогичным типом. Префикс b
предоставляет байтовый литерал, который эквивалентен u8
.
Вместо
buf != [b'q']
в этом случае можно написатьbuf[0] != b'q'
.
Канонический режим и режим Raw ⛓
По умолчанию ваш терминал запускается в каноническом режиме . Это означает, что ввод с клавиатуры отправляется в вашу программу после того, как вы нажмете Введите ключ
. Ввод также отображается в терминале, поэтому вы можете видеть, что вы набрали. Этот режим может быть полезен, так как вы можете редактировать то, что вы набрали, и использовать такие клавиши, как удалить
и назад
для редактирования, прежде чем вы нажмете Введите
, чтобы программа прочитала ваш ввод.
Нам нужен необработанный режим , , который полностью противоположен каноническому режиму . К счастью, крейт crossterm
предоставляет простой способ перевести терминал в необработанный режим. Напомним, что в первой части мы добавили кросстерм
зависимость.
Поскольку мы используем крейт
crossterm
, нам не нужно самостоятельно вручную изменять атрибуты терминала.
Включить необработанный режим 🔓
Как уже упоминалось, функция включения необработанного режима находится в crossterm
crate:
Теперь давайте попробуем. Скомпилируйте и запустите программу. Вы поймете, что клавиши, которые вы набираете, больше не отображаются. Вы также поймете, что вам не нужно нажимать q
и затем Введите
для выхода из программы. Только нажатие q
приводит к выходу из программы. Вы также можете заметить, что ввод не отображается на терминале даже после выхода из программы. Давайте изменим код, чтобы восстановить состояние терминала при выходе из программы.
Отключить режим Raw 🔐
Может показаться, что добавление terminal::disable_raw_mode()
в последнюю строку нашего кода может помочь, но это внесет ошибку в наш код. Давайте попробуем.
Теперь предположим, что функция после enable_raw_made()
выдает ошибку и паникует, disable_raw_mode()
не будет вызываться и терминал останется в режиме raw . Попробуйте, поместив panic!("Произошла ошибка")
в любом месте после terminal::enable_raw_mode()
. Чтобы исправить это, давайте создадим структуру
с именем CleanUp
:
. Давайте разберем ее. Мы создали новую структуру CleanUp
и реализовали для нее Drop. Сейчас drop()
вызывается в таких случаях, когда экземпляр структуры (в нашем случае переменная _clean_up
) обычно выходит за пределы области видимости (в данном случае, когда main() возвращает
) или когда есть panic , пока экземпляр все еще находится в области действия. Запустите программу и наблюдайте разницу по сравнению с ошибкой
Строка
panic!()
может быть добавлена в любом месте после строкиterminal::enable::raw_mode()
, чтобы наблюдать аналогичные эффекты
Теперь мы уверены, что терминал возвращается в исходное состояние после выхода из нашей программы, мы можем убрать панику !()
.
Возможно, вы также заметили, что Ctrl+D и Ctrl+C не завершают программу снова. Только q
завершает программу. Давайте посмотрим, что происходит под капотом
Отображение нажатий клавиш 📟
Измените код:
.is_control()
проверяет, является ли символ управляющим. Управляющие символы — это непечатаемые символы, которые мы не хотим выводить на экран. Все коды ASCII 0–31 являются управляющими символами, а 127 также является управляющим символом. Все коды ASCII 32–126 можно распечатать. (Проверьте таблицу ASCII, чтобы увидеть все символы)
Обратите внимание, что мы используем println!()
с \r
в качестве последнего аргумента. Это потому, что мы включили необработанный режим. Удалите \r
вот так и обратите внимание на разницу:
Используя println!()
без \r
, вы увидите, что символы новой строки, которые мы печатаем, перемещают курсор только вниз, а не влево сторона экрана. Это постоянно перемещает вывод вправо. \r
перемещает курсор обратно в левую часть экрана.
Теперь пришло время попробовать нашу программу и открыть для себя кое-что. Запустите программу и попробуйте клавиши, такие как клавиши со стрелками, или Escape
, или Page Up
, или Page Down
, или Home
, или End
, или Backspace
, или Delete
, или Введите
. Попробуйте комбинации клавиш с Ctrl, такие как Ctrl-A, Ctrl-B и т. д.
Вы заметите несколько интересных вещей:
- Клавиши со стрелками, Page Up, Page Down, Home и End all input 3 или 4 байта до терминала:
27
,'['
, а затем один или два других символа. Это известно как управляющая последовательность . Все управляющие последовательности начинаются с27
байт. Нажатие Escape отправляет на вход один байт27
. - Backspace — это байт
127
. Удалить — это 4-байтовая управляющая последовательность. - Введите либо байт
10
, символ новой строки, также известный как'\n'
, либо байт13
, символ возврата каретки, также известный как\r
. - Ctrl-A — это
1
, Ctrl-B — это2
, Ctrl-C — это… о, это завершает программу, верно. Но комбинации клавиш Ctrl, которые действительно работают, похоже, сопоставляют буквы A–Z с кодами 1–26
Теперь мы знаем, как различные нажатия клавиш преобразуются в байты.
Миграция проекта для использования crossterm 🦾
Может быть сложно помнить обо всех различных ключах и соответствующих им байтовых литералах. Вы можете продолжать ссылаться, но это может занять много времени. crossterm
crate также обеспечивает абстракцию различных ключевых событий, поэтому нам не нужно помнить, что Arrow Up
сопоставляется с 27[A
и так далее.
Давайте воспользуемся возможностями crossterm
:
Вот это уже много. Давайте попробуем обдумать это. loop { .. }
работает аналогично while()
. Мы перенесли наше условие из функции while
и поместили его в цикл 9.0008, чтобы сделать его более читабельным. Мы используем
crossterm::event::read()
вместо стандартного std::io::read()
. Это упрощает интерпретацию событий с терминала. event::read()
возвращает событие Event
, которое является перечислением
. Поскольку нас сейчас интересуют только нажатия клавиш, мы проверяем, является ли возвращенное событие Event
Key
. Затем мы проверяем, является ли нажатая клавиша q
. Если пользователь нажал q
, мы выходим из цикла
и программа завершается.
Запустите программу и введите различные комбинации клавиш. Это поможет понять, как работает программа.
Тайм-аут для
read()
⏳ В настоящее время read()
будет бесконечно ждать ввода с клавиатуры перед возвратом. Что, если мы хотим сделать что-то вроде анимации на экране, ожидая ввода пользователя? Или что, если мы хотим завершить программу после некоторого времени бездействия? Мы можем установить тайм-аут, чтобы read()
возвращает значение, если в течение определенного времени не поступает никаких данных. Мы будем использовать функцию crossterm::event::poll
вместе с read
:
event::poll
проверяет, доступно ли событие Event
в течение заданного времени. Поиграйте с заданным временем. Если Событие
не доступно в течение заданного времени, poll
возвращает false
.
Обработка ошибок 🚫
Поскольку мы закончили перенос нашего проекта на использование crossterm
, давайте обработаем возможные ошибки. До сих пор мы использовали .expect(" .. ")
. Необходимость писать .expect()
каждый раз может утомлять. Давайте воспользуемся сокращением, предоставленным Rust. Давайте изменим нашу функцию main()
следующим образом:
? Оператор
можно использовать только в методе, который возвращает Result
или Option
, поэтому нам нужно изменить наш main()
, чтобы он возвращал Result
. crossterm::Result
можно расширить до std::result::Result
. Таким образом, для нашей функции main()
возвращаемый тип может быть преобразован в std::result::Result<(), std::io::Error>
. Возьмем, к примеру, функцию poll
. Он возвращает std::result::Result
. Поскольку второй аргумент в Результат
возвращен опросом
(т.е. std::io::Error
) того же типа, что и Result
, возвращаемый main()
, мы можем использовать ? Оператор
в функции poll
в методе main()
.
Давайте завершим эту часть урока быстрым обзором того, что мы сделали. Сначала мы считываем нажатия клавиш, затем модифицируем программу так, чтобы она завершалась при нажатии q
. Затем мы включили необработанный режим (что позволило нам увидеть, как различные ключи, такие как Home
, преобразуются в байты) , а также нашел способ правильно отключить необработанный режим при выходе из нашей программы. Мы также интегрировали crossterm
в наш проект. Наконец, мы изменили способ обработки ошибок.
В следующей части мы сделаем более низкоуровневую обработку ввода и вывода терминала и используем ее для рисования на экране и предоставления пользователю возможности перемещать курсор!
Если вам нужно больше примеров кода Rust, вот рекомендуемый
Rust Cookbook и Учебник вы должны попробовать!Виртуальный Паскаль | eCSoft/2
Virtual Pascal v2. 1 Beta Build 279 Лондон, май 2004 г. (С) Copyright 2004 vpascal.com Этот файл предназначен для того, чтобы дать обзор того, что изменилось между Virtual Pascal v2.1 Beta build 243/270/274 и эта сборка. Упоминаются как исправления ошибок, так и новые функции. Где ошибка новой функции присвоен номер ссылки, номер ссылки также упоминается. Большинство изменений упоминается в этом документе. Для более подробного описания каждой проблемы, обратитесь к базе данных ошибок VP, которую можно найти по адресу http://www.vpascal.com/bugs. Исправлены ошибки в сборке 277 ---------------------- [98] Разные проблемы с SysCmdLn в Windows. [107] Readkey не получает весь вставленный текст. [108] Ряд проблем с вводом символов в Windows, включая использование AltGr/право-Alt [110] Ошибка в выравнивании записей в ShellApi.pas. [111] Ошибка в диалоговом окне открытия Turbo Vision (stddlg.
pas). [125] HeapChk.pas работал некорректно. [134] Файлы, созданные в Linux, всегда имеют атрибут Executable. [147] WinDos.GetEnvVar всегда возвращал мусор в Windows. Исправлены ошибки в сборке 274 ---------------------- [42] Исправлено определение API DeviceCapabilities в win32.def. Он был внесен в список как API в winspool.dll — на самом деле он существует в winspool.drv. [43] В Win32 использовались несколько функций преобразования кодовых страниц Ansi/Oem. для обработки перевода набора символов для консольных приложений. Теперь это были удалены, поэтому внутренние компоненты VpSysW32 используют строки кодовой страницы OEM путем по умолчанию при использовании в консольном приложении. В результате имена файлов с символы high-ascii в имени должны работать лучше, чем раньше. [74] IDE и компилятор теперь загружают локализованный строковый файл, если он доступен.