Haskell ввод числа с клавиатуры

Haskell ленивый язык программирования

Haskell — необычный язык с точки зрения тех, кто привык к JavaScript, С++, Python или любому другому императивному языку. Всё дело в том, что Haskell — функциональный язык. Мы уже рассказывали, чем отличаются функциональные языки от остальных, теперь посмотрим на них в деле.

👉 Haskell — не совсем для обычных вещей, и стандартное приложение с красивым интерфейсом на нём сделать не получится. А вот сделать серверную часть, которая возьмёт на себя все сложные вычисления, или консольную программу, которая вызывается через командную строку, — вполне. Лучше всего Haskell справляется с точными вычислениями и расчётами, поэтому чем лучше вы будете знать математику — тем лучше для кода.

Синтаксис в Haskell

Самая простая программа на Haskell выглядит так:

main = putStrLn «Hello World!»

Давайте разберём, как работает единственная строка в программе. Main — это как бы главная функция, которая возвращает какое-то значение. В нашем случае оно возвращает значение вывода на экран строки «Hello World!». Компилятор знает, что putStrLn — функция вывода на экран, но если бы он этого не знал, нам нужно было бы написать так:

main = putStrLn «Hello World!»

Теперь мы явно указали тип функции main — это ввод и вывод каких-то результатов (IO — input/output, ввод-вывод). Ещё в Haskell есть такие базовые типы данных:

  • Integer — целое число
  • Char — символ
  • Double — число с плавающей точкой или дробное число

А ещё есть специальные конструкции (), [] и ->, с помощью которых можно сделать свои типы данных на основе базовых, например, список или массив.

Математические операции в Haskell работают и выглядят как обычно: +, –, *, /.

Функции и их значения

Почти всё в Haskell делается через функции. Задача программиста — описать функцию таким образом, чтобы компилятор понял:

  • какие параметры могут прийти в функцию,
  • что с ними нужно сделать,
  • в каком виде нужно отдать результат.

Получается, что функции — это наборы правил, по которым обрабатываются данные. Например, если нам нужно найти факториал любого числа, то на Haskell это можно записать так:

Первая строка после комментариев говорит нам, что мы объявляем новую функцию (fac),и работать с типами данных (::) она будет так: на вход ей поступит целое число (Integer) и на выходе функция вернёт тоже целое число (-> Integer).

Вторая строка отвечает за то, что если нужно посчитать факториал ноля, то это будет единица.

Последняя строка — самая сложная и интересная. Следите за математической мыслью: факториал любого числа n (fac n), где n больше нуля (| n > 0), равен произведению этого числа (n *) на факториал предыдущего числа (fac (n – 1)).

Если пока непонятно, что тут происходит, — почитайте про рекурсию, там мы подробно разобрали этот пример.

Мы только что записали строгое математическое определение на Haskell и объяснили функции, как ей найти факториал любого числа. Но если мы попробуем вызвать факториал числа 4.5, то Haskell выдаст ошибку, потому что 4.5 не относится к типу Integer.

👉 Вся программа на Haskell — это набор таких функций и правил, которые могут быть связаны друг с другом. Все данные обрабатываются строго по этим правилам, и на выходе получается точный результат. Можно работать с любыми данными, если перевести их в понятный для языка вид, поэтому Haskell часто используют в тех областях, где нужна надёжность и гарантированная точность вычислений.

Ленивые вычисления

Haskell — язык, который поддерживает ленивые вычисления. Это значит, что он вычислит нужные значения в любой функции не тогда, когда программист это написал, а когда это значение действительно понадобится в программе.

Например, у нас есть функция, которая возвращает какое-то значение после вызова. Если это значение прямо сейчас не нужно или оно не используется при вызове функции, то Haskell не будет его считать. Он дождётся того момента, когда значение функции понадобится в другом месте, и только тогда посчитает его.

Ленивые вычисления помогают сократить нагрузку на ресурсы и делают программы быстрее и эффективнее. Если вы пишете калькулятор, в котором предусматриваете все математические действия, но пользуетесь только сложением, то Haskell даже не будет обращать внимания на остальное. Он будет знать, что у вас есть код, который, если что, может ещё умножать и делить, но делать с ним пока ничего не будет.

Для чего нужен Haskell

Обработка текста и синтаксический анализ. В Haskell легко заложить правила любого языка, по которым строятся языковые конструкции, и научить его анализировать этот язык. Например, он может делить предложения на слова и предлоги, находить связи между ними, смотреть, всё ли написано без ошибок, или находить неправильные языковые конструкции. Это работает и для обычных языков (русский или английский), и для языков программирования.

Компиляторы. Благодаря тому, что Haskell всё делает строго по правилам, это отличный инструмент для написания компиляторов. Задача любого компилятора — взять текст программы, проверить, нет ли в нём ошибок, и выполнить код. Так как Haskell отлично работает с текстом и при этом не ошибается в правилах, то он может преобразовывать команды другого языка в команды для компьютера.

Читайте также:  Инструкция по разборке ноутбука Dell Inspiron M5010

Финансовые инструменты. Основное требования к финансовым инструментам — гарантированная точность вычислений и отсутствие ошибок, поэтому Haskell любят использовать корпорации для своих внутренних инструментов. Это могут быть системы учёта банковских транзакций, биржевой торговли, анализа рисков или инструменты финансового мониторинга между отделами.

Промышленные приложения. Haskell позволяет задавать нужные правила и обрабатывать данные по этим правилам — именно это нужно предприятиям, чтобы построить системы поддержки принятия решений или внутреннего аудита. Это снимает нагрузку с людей и позволяет алгоритмам эффективнее находить слабые места в компании или точки для промышленного роста.

Текст:
Михаил Полянин Редактор:
Михаил Полянин Корректор:
Ирина Михеева Иллюстрация:
Даня Берковский Вёрстка:
Мария Дронова Глашатай:
Виталий Вебер Лениво вычисляет:
Родион Скрябин

Источник



Основы функционального программирования/Haskell/Ввод-вывод

В Haskell, как и во всех остальных языках, существует всесторонняя система операций ввода-вывода. Хотя обычно операции ввода-вывода предполагают некоторую последовательность в своём выполнении, то есть по сути дела императивность, в Haskell система операций ввода-вывода полностью поддерживает функциональную парадигму программирования.

Уже говорилось, что все операции ввода-вывода построены при помощи такого понятия языка Haskell, как монада (лекция 6). В то же время для понимания системы операций ввода-вывода в Haskell нет особой необходимости понимать теоретические основы понятия монада. Монады можно рассматривать как концептуальные рамки, в которых содержится система ввода-вывода. Можно сказать, что понимание теории категорий так же необходимо для использования системы операций ввода-вывода в Haskell, как и понимание теории групп для выполнения арифметических операций.

Операции ввода-вывода в любом языке основаны на понятии действия. При возбуждении действия оно выполняется. Однако в Haskell действия не возбуждаются, а скорее просто декларируются. В свою очередь действия могут быть атомарными или составленными из последовательности других действий. Монада IO содержит операции, которые позволяют создавать сложные действия из атомарных. То есть монаду в данном случае можно рассматривать как клей, который связывает действия в программе.

Базовые операции ввода-вывода [ править ]

Каждое действие ввода-вывода возвращает какое-то значение. Для того, чтобы различать эти значения от базовых, типы этих значений как бы обёрнуты типом IO (необходимо помнить, что монада является контейнерным типом). Например, тип функции getChar следующий:

В этом примере показано, что функция getChar выполняет некоторое действие, которое возвращает значение типа Char . Действия, которые не возвращают ничего интересного, имеют тип IO () . То есть символ () обозначает пустой тип ( void в других языках). Так функция putChar имеет тип:

Друг с другом действия связываются при помощи оператора связывания. То есть символы >>= выстраивают последовательность действий. Как известно, вместо этой функции можно использовать служебное слово do . Оно использует такой же двумерный синтаксис, как и слова let и where , поэтому можно не использовать для разделения вызова функций символ ; . При помощи слова do можно связывать вызовы функций, определение данных (при помощи символов -> ) и множество определений локальных переменных (служебное слово let ).

Например, так можно определить программу, которая читает символ с клавиатуры и выводит его на экран:

В этом примере не случайно для имени функции выбрано слово main . В Haskell, также, как и в языках Си/Си++ название функции main используется для обозначения точки входа в программу. Кроме того, в Haskell тип функции main должен быть типом монады IO (обычно используется IO () ). Ко всему прочему, точка входа в виде функции main должна быть определена в модуле с именем Main .

Пусть имеется функция ready , которая должна возвращать True , если нажата клавиша «y», и False в остальных случаях. Нельзя просто написать:

Потому что в этом случае результатом выполнения операции сравнения будет значение типа Bool , а не IO Bool . Для того, чтобы возвращать монадические значения, существует специальная функция return , которая из простого типа данных делает монадический. То есть в предыдущем примере последняя строка определения функции ready должна была выглядеть как return (c == ‘y’) .

В следующем примере показана более сложная функция, которая считывает строку символов с клавиатуры:

Пример 16. Функция getLine .

Необходимо помнить, что в тот момент, когда программист перешёл в мир действий (использовал систему операций ввода-вывода), назад пути нет. То есть если функция не использует монадический тип IO , то она не может заниматься вводом/выводом, и наоборот, если функция возвращает монадический тип IO , то она должна подчиняться парадигме действий в Haskell.

Программирование при помощи действий [ править ]

Действия ввода-вывода являются обычными значениями в терминах Haskell. То есть действия можно передавать в функции в качестве параметров, заключать в структуры данных и вообще использовать там, где можно использовать данные языка Haskell. В этом смысле система операций ввода-вывода является полностью функциональной. Например, можно предположить список действий:

Этот список не возбуждает никаких действий, он просто содержит их описания. Для того, чтобы выполнить эту структуру, то есть возбудить все её действия, необходима некоторая функция (например, sequence_ ):

Читайте также:  Выключилась подсветка клавиатуры hyperx

Эта функция может быть полезна для написания функции putStr , которая выводит строку на экран:

На этом примере видно явное отличие системы операций ввода-вывода языка Haskell от систем императивных языков. Если бы в каком-нибудь императивном языке была бы функция map , она бы выполнила кучу действий. Вместо этого в Haskell просто создаётся список действий (одно для каждого символа строки), который потом обрабатывается функцией sequence_ для выполнения.

Обработка исключений [ править ]

Что делать, если в процессе операций ввода-вывода возникла неординарная ситуация? Например, функция getChar обнаружила конец файла. В этом случае произойдёт ошибка. Как и любой продвинутый язык программирования, Haskell предлагает для этих целей механизм обработки исключений. Для этого не используется какой-то специальный синтаксис, но есть специальный тип IOError , который содержит описания всех возникаемых в процессе ввода-вывода ошибок.

Обработчик исключений имеет тип (IOError -> IO a) , при этом функция catch ассоциирует (связывает) обработчик исключений с набором действий:

Аргументами этой функции являются действие (первый аргумент) и обработчик исключений (второй аргумент). Если действие выполнено успешно, то просто возвращается результат без возбуждения обработчика исключений. Если же в процессе выполнения действия возникла ошибка, то она передаётся обработчику исключений в качестве операнда типа IOError , после чего выполняется сам обработчик.

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

В этой программе видно, что можно использовать вложенные друг в друга обработчики ошибок. В функции getChar’ отлавливается ошибка, которая возникает при обнаружении символа конца файла. Если ошибка другая, то при помощи функции ioError она отправляется дальше и ловится обработчиком, который «сидит» в функции getLine’ . Для определённости в Haskell предусмотрен обработчик исключений по умолчанию, который находится на самом верхнем уровне вложенности. Если ошибка не поймана ни одним обработчиком, который написан в программе, то её ловит обработчик по умолчанию, который выводит на экран сообщение об ошибке и останавливает программу.

Файлы, каналы и обработчики [ править ]

Для работы с файлами Haskell предоставляет все возможности, что и другие языки программирования. Однако большинство этих возможностей определены в модуле IO , а не в Prelude , поэтому для работы с файлами необходимо явно импортировать модуль IO .

Открытие файла порождает обработчик (он имеет тип Handle ). Закрытие обработчика инициирует закрытие соответствующего файла. Обработчики могут быть также ассоциированы с каналами, то есть портами взаимодействия, которые не связаны напрямую с файлами. В Haskell предопределены три таких канала — stdin (стандартный канал ввода), stdout (стандартный канал вывода) и stderr (стандартный канал вывода сообщений об ошибках).

Таким образом, для использования файлов можно пользоваться следующими вещами:

Далее приводится пример программы, которая копирует один файл в другой:

Здесь использована одна интересная и важная функция — hGetContents , которая берёт содержимое переданного ей в качестве аргумента файла и возвращает его в качестве одной длинной строки.

Окончательные замечания [ править ]

Получается так, что в Haskell заново изобретено императивное программирование…

В некотором смысле — да. Монада IO встраивает в Haskell маленький императивный подъязык, при помощи которого можно осуществлять операции ввода-вывода. И написание программ на этом подъязыке выглядит обычно с точки зрения императивных языков. Но есть существенное различие: в Haskell нет специального синтаксиса для ввода в программный код императивных функций, всё осуществляется на уровне функциональной парадигмы. В то же время опытные программисты могут минимизировать императивный код, используя монаду IO только на верхних уровнях своих программ, так как в Haskell императивный и функциональный миры чётко разделены между собой. В отличие от Haskell, в императивных языках, в которых есть функциональные подъязыки, нет чёткого разделения между обозначенными мирами.

Источник

Haskell ввод числа с клавиатуры

K�����]�-��ί@�Kդ�� E���e�Y���-w��X+��C?-7�󻣁G���v�|�?m �Z���NBv������ϯ������)A����62�Q.�uW�E��W*’�7�S`� endstream endobj 40 0 obj > endobj 41 0 obj > stream endstream endobj 42 0 obj > stream 1.00028 0 0 1.00028 72 769.82 cm endstream endobj 43 0 obj > stream xڥ[I����W̱x��R$�>I�� r�-�A�’��6��C�B�작txқaq���b��Y���t���z���w�ۛM������Y����!��?�ާ�O�|p���

Yl����޽_^�p����%X&�=�����4�������K������?�138����k�t8�|X�ݫ ��Z��d�@Q�֕����w�g���q����̜�­͇���L���t��l��Z��݇n�������˽�X�SL�I�qW7�̹��M��R����2qB]�Ju���#�/�F�������폪o��/�Ŭ vr <�ch��7�fQ ���|v]p�FA2Gb��;|b& �t͝ K�ca��y)�A�B�6�����)�� endstream endobj 61 0 obj >stream BT /F6 4 Tf 1 0 0 1 50 5 Tm 0.70588 0.70588 0.70588 rg (View publication stats) Tj ET BT /F6 4 Tf 1 0 0 1 50 5 Tm 0.70588 0.70588 0.70588 rg (View publication stats) Tj ET endstream endobj 62 0 obj > endobj 63 0 obj > stream x���1�\e��ј��h)X���܂����2�Hg!���8�D����»��l�7x�g�������x��-�_�����2���� �����9<<�j�z��bz�Ξ��oY

���I��ps;=b��M�w�9?��������typߘ�˳�����%ٙ endstream endobj 64 0 obj > /W [0 [365.23438] 5 [201.66016] 13 14 302.73438 17 [248.53516 310.54688 248.53516 349.60938] 21 30 496.58203 31 [248.53516] 38 [543.94531 587.89063 570.80078 614.74609 526.85547 493.65234 0 0 262.69531 0 0 485.83984 726.5625 646.97266 663.57422 575.68359 0 580.56641 533.69141 535.64453 644.53125 514.64844] 70 [511.71875 554.6875 455.56641 554.6875 495.60547 291.99219 503.90625 543.94531 245.60547 246.58203 494.62891 254.88281 828.61328 546.875 541.99219 554.6875 549.80469 346.67969 418.94531 337.89063 543.94531 466.79688 717.77344 0 466.79688] 100 [201.66016] 123 [248.53516] ] /DW 0 >> endobj 65 0 obj > stream x�]R�j�0��+tl��� C�4�C��8�:Բ������n�@��ݙ�RԴ��hϣw7� ��� endstream endobj 66 0 obj > /W [0 [365.23438 0 0 230.95703] 29 [345.70313] 43 [833.98438 373.53516 0 0 606.93359 0 0 758.78906] 55 [621.58203] 68 [559.57031 0 0 620.60547 559.57031 0 587.89063 685.54688 0 0 622.55859 340.82031 0 682.61719 0 645.99609 0 0 511.71875 421.875 630.85938] ] /DW 0 >> endobj 67 0 obj > stream x�]�Mo�0 ���9v��ϖRK��a���a�F�Bz��/ش�)H���

Читайте также:  Простые кликеры для клавиатуры

��������Î��;���i�[���,��T­�_1��^�̓���������g’gg�9��/,x���=�\��ss7�Ў��(���wzm�[;P����+7o���k6�c�܈Q�dZ��=� �����o� l�;R������; �h�L� > /W [0 [365.23438] 5 [204.58984] 21 26 512.69531 31 [274.90234] 38 [557.61719] 56 [544.92188] 70 [522.94922 563.96484 461.91406 563.96484 506.83594 316.89453 519.53125 557.61719 261.71875 262.69531 521.97266 270.99609 842.77344 559.57031 548.82813 563.96484 0 372.55859 430.66406 360.83984 555.66406 0 747.55859] ] /DW 0 >> endobj 69 0 obj > stream x�]��j�0���s��X�[�^� �/�Cm@��j 1<��7�l�Ѐ���93�DM���!zs���è�r�.W'��I JK#z˹�" �n[=έQ��<���mp��2��^�B���Ϧ �]�����XT(��so_�!"ٱU���v �����"�� o#���%��L(�8� �K8�@���ϬF��;��Cw�q�SRe9Ӆ�&J�D��(<$�3��!* �ys�

g�WKxX�ڌg��7�ڙ��?�>Ez�e������ɫs!.�#�iOH�_�]�ڟ ��n endstream endobj 70 0 obj > /W [0 [600.09766] 935 [686.03516 609.86328] 941 [748.04688] 948 [751.95313 603.02734 0 0 0 860.83984] 965 [612.79297 616.69922 589.35547 525.39063 691.40625 615.23438 0 0 649.90234 0 604.00391 639.16016 0 653.80859 611.81641 0 634.76563 0 582.51953 591.79688] 992 [789.55078 589.35547] 2990 [1040.03906] ] /DW 0 >> endobj 71 0 obj > stream x�]R�j�0��+tL���4-� ��u���N�,d�࿯�MR�@����YFN���r4y�����Qiia�/V=�Yi�2*�pW�b I��_S�Ǚ�ɇ�.ήtS��$y���g��jz���1?0�v4%eI%�

��`^� he�N��r��k�:>W4���6b���A��H��S���OI@���N�����U�Ӝ=���eyD �8��]DyP] > endobj 73 0 obj > endobj 74 0 obj > endobj 75 0 obj > endobj 76 0 obj > endobj 77 0 obj > endobj 78 0 obj > endobj 79 0 obj > endobj 80 0 obj > endobj 81 0 obj > endobj 82 0 obj > endobj 83 0 obj > endobj 84 0 obj > endobj 85 0 obj > endobj 86 0 obj > endobj 87 0 obj > endobj 88 0 obj > endobj 89 0 obj > endobj 90 0 obj > endobj 91 0 obj > endobj 92 0 obj > endobj 93 0 obj > endobj 94 0 obj > endobj 95 0 obj > endobj 96 0 obj > endobj 97 0 obj > endobj 98 0 obj > stream x��< tS׹�����e����_�#��B(

Источник

Haskell читает исходный ввод с клавиатуры

Я пишу программу терминального режима в Haskell. Как я могу прочитать информацию о сыром keypress?

В частности, похоже, что есть что-то, предоставляющее средства редактирования строк поверх Haskell. Если я выполняю getLine , я, похоже, могу использовать стрелку вверх, чтобы получить предыдущие строки, отредактировать текст, и только когда я нажимаю Enter, текст становится видимым для самого приложения Haskell.

То, что мне нужно, — это возможность читать отдельные нажатия клавиш, поэтому я могу самостоятельно выполнять редактирование строк.

Возможно, мой вопрос был неясным. В принципе, я хочу построить что-то вроде Vi или Emacs (или Yi). Я уже знаю, что есть привязки терминалов, которые позволят мне притворяться в режиме консольного режима, поэтому выходная сторона не должна быть проблемой. Я просто ищу способ получить исходный ввод ввода, поэтому я могу делать такие вещи, как (например) добавить K в текущую строку текста, когда пользователь нажимает букву K или сохраняет файл на диск, когда пользователь нажимает Ctrl + S.

ОТВЕТЫ

Ответ 1

Похоже, вы хотите поддержку readline. Для этого есть несколько пакетов, но haskeline, вероятно, самый простой в использовании с наиболее поддерживаемыми платформами.

Ответ 2

Неполная:

После нескольких часов веб-серфинга я могу сообщить следующее:

readline имеет огромный интерфейс практически без документации. Из имен функций и подписи типов вы могли бы догадаться, что делает этот материал. но это далеко не тривиально. Во всяком случае, эта библиотека, кажется, обеспечивает интерфейс редактирования на высоком уровне — это то, что я пытаюсь реализовать сам. Мне нужно что-то более низкоуровневое.

После прохода через источник haskeline , похоже, у него огромный низкоуровневый код путаницы, отдельно для Win32 и POSIX. Если есть простой способ выполнить консольный ввод-вывод, эта библиотека не демонстрирует его. Код выглядит настолько тесно интегрированным и очень специфичным для haskeline , что я сомневаюсь, что смогу его повторно использовать. Но, возможно, читая его, я могу учиться достаточно, чтобы написать свой собственный?

Yi. бесстрашный массив. В файле Cabal перечислены > 150 открытых модулей. (!!) Похоже, что под ним используется пакет под названием vty , который является только POSIX. (Интересно, как, черт возьми, Yi работает на Windows?) vty выглядит так, как будто он может быть полезен для меня без дальнейшей модификации. (Но опять же, не в Windows.)

unix имеет. в принципе ничего интересного. У этого есть куча вещей, чтобы установить вещи на терминале, но абсолютно ничего для чтения с терминала. (Кроме того, чтобы проверить, включено ли эхо и т.д. Ничего о нажатиях клавиш.)

unix-compat не имеет абсолютно никакого интереса.

Ответ 3

Это может быть самое простое решение, похожее на типичный код на других языках программирования:

Он работает, читая более одного символа «за раз». Разрешение. ключ ↑ , который состоит из трех символов [‘\ESC’,'[‘,’A’] , которые следует отличать от фактического ввода символа \ESC .

Пример использования:

Это немного хакерское, поскольку теоретически пользователь может вводить больше ключей, прежде чем программа попадет на hReady . Что может произойти, если терминал позволяет вставить. Но на практике для интерактивного ввода это не реалистичный сценарий.

Фактор удовольствия: Строки курсора могут быть putStr d, чтобы на самом деле перемещать курсор программно.

Ответ 4

Один из вариантов заключается в использовании ncurses. Минималистский пример:

Ответ 5

Я думаю, что вы ищете hSetBuffering. По умолчанию StdIn привязан к строке, но вы хотите получить ключи сразу.

Ответ 6

Я думаю, что библиотека unix обеспечивает наиболее легкое решение для этого, особенно если вы знакомы с termios , что зеркалированный модулем System.Posix.Terminal .

На gnu.org хорошая страница, описывающая использование termios для настройки неканонического режима ввода для терминала и вы можете сделать это с помощью System.Posix.Terminal .

Здесь мое решение, которое преобразует вычисление в IO для использования неканонического режима:

Источник