Учебники

Haskell — Краткое руководство

Haskell — Обзор

Haskell — это язык функционального программирования, который был специально разработан для обработки символьных вычислений и обработки списков. Функциональное программирование основано на математических функциях. Помимо Haskell, некоторые из других популярных языков, которые следуют парадигме функционального программирования, включают: Lisp, Python, Erlang, Racket, F #, Clojure и т. Д.

В традиционном программировании инструкции принимаются как набор объявлений в определенном синтаксисе или формате, но в случае функционального программирования все вычисления рассматриваются как комбинация отдельных математических функций.

Работать с Haskell

Haskell — широко используемый чисто функциональный язык. Здесь мы перечислили несколько моментов, которые делают этот язык таким особенным по сравнению с другими традиционными языками программирования, такими как Java, C, C ++, PHP и т. Д.

  • Функциональный язык — на обычном языке программирования мы инструктируем компилятору ряд задач, которые просто говорят вашему компьютеру «что делать» и «как делать?» Но в Haskell мы скажем нашему компьютеру «что это?»

  • Лень — Хаскель — ленивый язык. Под ленивым мы подразумеваем, что Хаскелл не будет оценивать любое выражение без какой-либо причины. Когда механизм оценки обнаруживает, что выражение необходимо оценить, он создает структуру данных thunk для сбора всей необходимой информации для этой конкретной оценки и указатель на эту структуру данных thunk . Механизм оценки начнет работать только тогда, когда потребуется оценить это конкретное выражение.

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

  • Статически Типизированный — В обычном языке программирования нам нужно определить ряд переменных вместе с их типом. Напротив, Haskell — строго типизированный язык. Под термином «язык строго типизированных» мы подразумеваем, что компилятор Haskell достаточно умен, чтобы определить тип объявленной переменной, поэтому нам не нужно явно указывать тип используемой переменной.

  • Ремонтопригодность — приложения на Haskell являются модульными, и поэтому их очень легко и экономично обслуживать.

Функциональный язык — на обычном языке программирования мы инструктируем компилятору ряд задач, которые просто говорят вашему компьютеру «что делать» и «как делать?» Но в Haskell мы скажем нашему компьютеру «что это?»

Лень — Хаскель — ленивый язык. Под ленивым мы подразумеваем, что Хаскелл не будет оценивать любое выражение без какой-либо причины. Когда механизм оценки обнаруживает, что выражение необходимо оценить, он создает структуру данных thunk для сбора всей необходимой информации для этой конкретной оценки и указатель на эту структуру данных thunk . Механизм оценки начнет работать только тогда, когда потребуется оценить это конкретное выражение.

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

Статически Типизированный — В обычном языке программирования нам нужно определить ряд переменных вместе с их типом. Напротив, Haskell — строго типизированный язык. Под термином «язык строго типизированных» мы подразумеваем, что компилятор Haskell достаточно умен, чтобы определить тип объявленной переменной, поэтому нам не нужно явно указывать тип используемой переменной.

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

Функциональные программы более параллельны и выполняют параллелизм в выполнении, чтобы обеспечить более точную и лучшую производительность. Хаскелл не исключение; он был разработан таким образом, чтобы эффективно обрабатывать многопоточность .

Привет, мир

Это простой пример, демонстрирующий динамизм Хаскелла. Посмотрите на следующий код. Все, что нам нужно, это всего лишь одна строка, чтобы напечатать «Hello Word» на консоли.

Live Demo

main = putStrLn "Hello World"

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

Hello World 

В этом руководстве мы приведем множество примеров, чтобы продемонстрировать всю мощь и простоту Haskell.

Haskell — Настройка среды

Мы настроили среду программирования на Haskell по адресу: https://www.tutorialspoint.com/compile_haskell_online.php

В этом онлайн-редакторе есть много вариантов, чтобы попрактиковаться в программировании на Haskell. Перейдите в раздел терминала на странице и введите «ghci» . Эта команда автоматически загружает компилятор Haskell и запускает Haskell онлайн. Вы получите следующий вывод после использования команды ghci .

sh-4.3$ ghci
GHCi,version7.8.4:http://www.haskell.org/ghc/:?forhelp
Loading package ghc-prim...linking...done.
Loading packageinteger gmp...linking... done.
Loading package base...linking...done.
Prelude>

Если вы по-прежнему хотите использовать Haskell в автономном режиме в локальной системе, вам необходимо загрузить доступную настройку Haskell с его официальной веб-страницы — https://www.haskell.org/downloads.

На рынке доступны три типа инсталляторов:

  • Минимальный установщик — предоставляет GHC (компилятор Glasgow Haskell), CABAL (общая архитектура для создания приложений и библиотек) и инструменты стека.

  • Установщик стека. В этом установщике GHC можно загрузить в кроссплатформенной цепочке управляемых сборов. Он установит ваше приложение глобально, так что он может обновлять свои инструменты API при необходимости. Он автоматически разрешает все зависимости, ориентированные на Haskell.

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

Минимальный установщик — предоставляет GHC (компилятор Glasgow Haskell), CABAL (общая архитектура для создания приложений и библиотек) и инструменты стека.

Установщик стека. В этом установщике GHC можно загрузить в кроссплатформенной цепочке управляемых сборов. Он установит ваше приложение глобально, так что он может обновлять свои инструменты API при необходимости. Он автоматически разрешает все зависимости, ориентированные на Haskell.

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

Мы видели различные типы инсталляторов, доступных на рынке, теперь давайте посмотрим, как использовать эти инсталляторы на нашей машине. В этом руководстве мы собираемся использовать установщик платформы Haskell для установки компилятора Haskell в нашей системе.

Настройка среды в Windows

Чтобы настроить среду Haskell на своем компьютере с Windows, перейдите на их официальный веб-сайт https://www.haskell.org/platform/windows.html и загрузите установщик в соответствии с вашей настраиваемой архитектурой.

Установщик Windows

Проверьте архитектуру вашей системы, загрузите соответствующий установочный файл и запустите его. Он будет установлен как любое другое приложение Windows. Возможно, вам придется обновить конфигурацию CABAL вашей системы.

Настройка среды в MAC

Чтобы настроить среду Haskell в своей системе MAC, перейдите на их официальный веб-сайт https://www.haskell.org/platform/mac.html и загрузите установщик Mac.

Установщик MAC

Настройка среды в Linux

Установка Haskell в системе на основе Linux требует запуска некоторой команды, которая не так проста, как MAC и Windows. Да, это утомительно, но это надежно.

Вы можете следовать инструкциям ниже, чтобы установить Haskell в вашей системе Linux —

Шаг 1 — Чтобы настроить среду Haskell в вашей системе Linux, перейдите на официальный сайт https://www.haskell.org/platform/linux.html и выберите свой дистрибутив. Вы найдете следующий экран в вашем браузере.

Установщик Linux

Шаг 2 — Выберите ваш дистрибутив. В нашем случае мы используем Ubuntu. Выбрав эту опцию, вы получите следующую страницу на вашем экране с командой для установки Haskell в нашей локальной системе.

Установщик Ubuntu

Шаг 3 — Откройте терминал, нажав Ctrl + Alt + T. Запустите команду «$ sudo apt-get install haskell-platform» и нажмите Enter. Он автоматически начнет загрузку Haskell в вашей системе после аутентификации с помощью пароля root. После установки вы получите подтверждающее сообщение.

Шаг 4 — Снова зайдите в свой терминал и выполните команду GHCI. Как только вы получите приглашение Prelude, вы готовы использовать Haskell в вашей локальной системе.

Локальная система

Чтобы выйти из пролога GHCI, вы можете использовать команду «: quit exit».

Haskell — Основные модели данных

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

В этом руководстве мы будем использовать онлайн-платформу Haskell, доступную на нашем веб-сайте ( https://www.tutorialspoint.com/codingground.htm ).

чисел

Haskell достаточно умен, чтобы декодировать некоторое число как число. Поэтому вам не нужно упоминать его тип внешне, как мы обычно делаем в случае других языков программирования. В соответствии с примером перейдите к командной строке прелюдии и просто запустите «2 + 2» и нажмите Enter.

sh-4.3$ ghci 
GHCi, version 7.6.3: http://www.haskell.org/ghc/  😕 for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 
Prelude> 2+2

В результате вы получите следующий вывод.

4

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

Теперь давайте попробуем немного более сложные математические вычисления и посмотрим, даст ли наш интеллектуальный компилятор правильный вывод или нет. Попробуйте с «15+ (5 * 5) -40»

Prelude> 15+(5*5)-40 

Вышеупомянутое выражение выдает «0» согласно ожидаемому результату.

0

Персонажи

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

Давайте предоставим следующую строку в качестве входных данных и проверим ее вывод.

Prelude> :t "a" 

Это даст следующий результат —

"a" :: [Char] 

Помните, что вы используете (: t ) при подаче ввода. В приведенном выше примере (: t) должен включать конкретный тип, связанный с входными данными. Мы узнаем больше об этом типе в следующих главах.

Взгляните на следующий пример, где мы передаем недопустимый ввод в виде символа, что, в свою очередь, приводит к ошибке.

Prelude> :t a 
<interactive>:1:1: Not in scope: 'a'  

Prelude> a 
<interactive>:4:1: Not in scope: 'a' 

В сообщении об ошибке «<интерактивный>: 4: 1: за пределами области действия:« a »» компилятор Haskell предупреждает нас, что он не может распознать ваш ввод. Haskell — это тип языка, где все представлено числом.

Haskell следует традиционному стилю кодирования ASCII. Давайте посмотрим на следующий пример, чтобы понять больше —

Prelude> '\97' 
'a'  
Prelude> '\67' 
'C' 

Посмотрите, как ваши входные данные декодируются в формат ASCII.

строка

Строка — это не что иное, как набор символов. Нет определенного синтаксиса для использования строки, но Haskell следует общепринятому стилю представления строки в двойных кавычках.

Посмотрите на следующий пример, где мы передаем строку «Tutorialspoint.com».

Prelude> :t "tutorialspoint.com" 

Он выдаст следующий вывод на экран —

"tutorialspoint.com" :: [Char] 

Посмотрите, как вся строка была декодирована как массив только Char. Давайте перейдем к другому типу данных и его синтаксису. Как только мы начнем нашу практику, мы привыкнем ко всем типам данных и их использованию.

логический

Булев тип данных также довольно прост как другие типы данных. Посмотрите на следующий пример, где мы будем использовать различные логические операции с использованием некоторых логических входов, таких как «True» или «False».

Prelude> True && True 
True  
Prelude> True && False 
False   
Prelude> True || True 
True  
Prelude> True || False 
True

В приведенном выше примере нам не нужно упоминать, что «True» и «False» являются логическими значениями. Сам Haskell может декодировать его и выполнять соответствующие операции. Давайте изменим наши входные данные на «истина» или «ложь».

Prelude> true 

Это даст следующий результат —

<interactive>:9:1: Not in scope: 'true' 

В приведенном выше примере Haskell не может различить «true» и числовое значение, поэтому наш ввод «true» не является числом. Следовательно, компилятор Haskell выдает ошибку, утверждая, что наш ввод не является его областью действия.

Список и понимание списка

Как и другие типы данных, List также является очень полезным типом данных, используемым в Haskell. В соответствии с примером, [a, b, c] является списком символов, следовательно, по определению, List является набором данных того же типа данных, разделенных запятой.

Как и другие типы данных, вам не нужно объявлять список как список. Haskell достаточно умен, чтобы декодировать ваш ввод, посмотрев на синтаксис, использованный в выражении.

Посмотрите на следующий пример, который показывает, как Haskell обрабатывает список.

Prelude> [1,2,3,4,5] 

Это даст следующий результат —

[1,2,3,4,5] 

Списки в Haskell по своей природе однородны, что означает, что они не позволят вам объявить список типов данных различного типа. Любой список типа [1,2,3,4,5, a, b, c, d, e, f] выдаст ошибку.

Prelude> [1,2,3,4,5,a,b,c,d,e,f] 

Этот код выдаст следующую ошибку —

<interactive>:17:12: Not in scope: 'a' 
<interactive>:17:14: Not in scope: 'b' 
<interactive>:17:16: Not in scope: 'c' 
<interactive>:17:18: Not in scope: 'd' 
<interactive>:17:20: Not in scope: 'e' 
<interactive>:17:22: Not in scope: 'f'

Понимание списка

Понимание списка — это процесс создания списка с использованием математического выражения. Посмотрите на следующий пример, где мы генерируем список, используя математическое выражение в формате [output | ассортимент, состояние.

Prelude> [x*2| x<-[1..10]] 
[2,4,6,8,10,12,14,16,18,20]  
Prelude> [x*2| x<-[1..5]] 
[2,4,6,8,10]  
Prelude> [x| x<-[1..5]] 
[1,2,3,4,5]

Этот метод создания одного списка с использованием математического выражения называется списком .

Кортеж

Haskell предоставляет еще один способ объявить несколько значений в одном типе данных. Это известно как Tuple . Кортеж можно рассматривать как список, однако существуют некоторые технические различия между кортежем и списком.

Tuple — это неизменяемый тип данных, так как мы не можем изменять количество элементов во время выполнения, тогда как List — это изменяемый тип данных.

С другой стороны, List — это однородный тип данных, но Tuple по своей природе неоднороден, поскольку Tuple может содержать внутри себя данные другого типа.

Кортежи представлены одной круглой скобкой. Посмотрите на следующий пример, чтобы увидеть, как Haskell обращается с кортежем.

Prelude> (1,1,'a') 

Это даст следующий результат —

(1,1,'a') 

В приведенном выше примере мы использовали один кортеж с двумя переменными числового типа и переменную типа char .

Haskell — Основные операторы

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

В этой главе мы будем использовать различные операторы в Haskell, используя нашу онлайн-платформу ( https://www.tutorialspoint.com/codingground.htm ). Помните, что мы используем только целые числа, потому что мы узнаем больше о десятичных числах в следующих главах.

Оператор сложения

Как следует из названия, оператор сложения (+) используется для функции сложения. Следующий пример кода показывает, как вы можете добавить два целых числа в Haskell —

Live Demo

main = do 
   let var1 = 2 
   let var2 = 3 
   putStrLn "The addition of the two numbers is:" 
   print(var1 + var2) 

В приведенном выше файле мы создали две отдельные переменные var1 и var2 . В конце мы печатаем результат с помощью оператора сложения . Используйте кнопку компиляции и выполнения для запуска вашего кода.

Этот код выведет следующий вывод на экран —

The addition of the two numbers is:
5

Оператор вычитания

Как следует из названия, этот оператор используется для операции вычитания. В следующем примере кода показано, как вы можете вычесть два целых числа в Haskell —

Live Demo

main = do 
   let var1 = 10 
   let var2 = 6 
   putStrLn "The Subtraction of the two numbers is:" 
   print(var1 - var2)

В этом примере мы создали две переменные var1 и var2 . После этого мы используем оператор вычитания (-), чтобы вычесть два значения.

Этот код выведет следующий вывод на экран —

The Subtraction of the two numbers is:
4

Оператор умножения

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

Live Demo

main = do 
   let var1 = 2 
   let var2 = 3 
   putStrLn "The Multiplication of the Two Numbers is:" 
   print(var1 * var2) 

Этот код выдаст следующий вывод, когда вы запустите его на нашей онлайн-платформе —

The Multiplication of the Two Numbers is:
6 

Оператор отдела

Посмотрите на следующий код. Он показывает, как вы можете разделить два числа в Haskell —

Live Demo

main = do 
   let var1 = 12 
   let var2 = 3 
   putStrLn "The Division of the Two Numbers is:" 
   print(var1/var2)

Это даст следующий результат —

The Division of the Two Numbers is: 
4.0 

Оператор последовательности / диапазона

Sequence или Range — это специальный оператор в Haskell. Обозначается «(..)». Вы можете использовать этот оператор при объявлении списка с последовательностью значений.

Если вы хотите распечатать все значения от 1 до 10, то вы можете использовать что-то вроде «[1..10]». Точно так же, если вы хотите сгенерировать все алфавиты от «a» до «z», то вы можете просто набрать «[a..z]» .

Следующий код показывает, как вы можете использовать оператор Sequence для печати всех значений от 1 до 10 —

Live Demo

main :: IO() 
main = do 
   print [1..10]

Это сгенерирует следующий вывод —

[1,2,3,4,5,6,7,8,9,10] 

Haskell — Принятие решений

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

Состояние цикла

Haskell предоставляет следующие типы решений для принятия решений —

Sr.No. Заявление и описание
1 оператор if-else

Один оператор if с оператором else . Инструкция в блоке else будет выполняться только тогда, когда данное булево условие не будет выполнено.

2 Вложенное выражение if-else

Несколько блоков if, за которыми следуют блоки else

Один оператор if с оператором else . Инструкция в блоке else будет выполняться только тогда, когда данное булево условие не будет выполнено.

Несколько блоков if, за которыми следуют блоки else

Haskell — Типы и Тип Класс

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

Класс встроенного типа

В Haskell каждое утверждение рассматривается как математическое выражение, а категория этого выражения называется типом . Вы можете сказать, что «Тип» — это тип данных выражения, используемого во время компиляции.

Чтобы узнать больше о типе , мы будем использовать команду «: t». В общем случае Type можно рассматривать как значение, тогда как Type Class можно рассматривать как набор похожих типов. В этой главе мы узнаем о различных встроенных типах.

Int

Int является классом типа, представляющим данные типа Integer. Каждое целое число в диапазоне от 2147483647 до -2147483647 относится к классу типа Int . В следующем примере функция fType () будет вести себя в соответствии с определенным типом.

Live Demo

fType :: Int -> Int -> Int 
fType x y = x*x + y*y
main = print (fType 2 4) 

Здесь мы установили тип функции fType () как int . Функция принимает два значения типа int и возвращает одно значение типа int . Если вы скомпилируете и выполните этот фрагмент кода, он выдаст следующий вывод:

sh-4.3$ ghc -O2 --make *.hs -o main -threaded -rtsopts 
sh-4.3$ main
20

целое число

Целое число можно рассматривать как надмножество Int . Это значение не ограничено никаким числом, поэтому целое число может иметь любую длину без каких-либо ограничений. Чтобы увидеть основное различие между типами Int и Integer , давайте изменим приведенный выше код следующим образом:

Live Demo

fType :: Int -> Int -> Int 
fType x y = x*x + y*y 
main = print (fType 212124454 44545454454554545445454544545)

Если вы скомпилируете указанный выше фрагмент кода, появится следующее сообщение об ошибке:

main.hs:3:31: Warning:            
   Literal 44545454454554545445454544545 is out of the Int range -
   9223372036854775808..9223372036854775807 
Linking main ...

Эта ошибка произошла из-за того, что наша функция fType () ожидала одно значение типа Int, и мы передали какое-то очень большое значение типа Int. Чтобы избежать этой ошибки, давайте изменим тип «Int» на «Integer» и наблюдаем разницу.

Live Demo

fType :: Integer -> Integer -> Integer 
fType x y = x*x + y*y 
main = print (fType 212124454 4454545445455454545445445454544545) 

Теперь он выдаст следующий результат:

sh-4.3$ main
1984297512562793395882644631364297686099210302577374055141

терка

Посмотрите на следующий фрагмент кода. Он показывает, как работает тип Float в Haskell —

Live Demo

fType :: Float -> Float -> Float 
fType x y = x*x + y*y 
main = print (fType 2.5 3.8)

Функция принимает два значения с плавающей запятой в качестве входных данных и возвращает другое значение с плавающей запятой в качестве выходных данных. Когда вы скомпилируете и выполните этот код, он выдаст следующий вывод:

sh-4.3$ main
20.689999 

двойной

Double — число с плавающей запятой с двойной точностью в конце. Взгляните на следующий пример —

Live Demo

fType :: Double -> Double -> Double 
fType x y = x*x + y*y 
main = print (fType 2.56 3.81)

Когда вы выполните приведенный выше фрагмент кода, он сгенерирует следующий вывод:

sh-4.3$ main 
21.0697

Bool

Bool — это логический тип. Это может быть как True, так и False. Выполните следующий код, чтобы понять, как работает тип Bool в Haskell:

Live Demo

main = do  
   let x = True 
   
   if x == False 
      then putStrLn "X matches with Bool Type" 
   else putStrLn "X is not a Bool Type" 

Здесь мы определяем переменную «x» как Bool и сравниваем ее с другим логическим значением, чтобы проверить ее оригинальность. Это даст следующий результат —

sh-4.3$ main
X is not a Bool Type 

голец

Чар представляет персонажей. Все, что находится в одинарной кавычке, считается символом. В следующем коде мы изменили нашу предыдущую функцию fType (), чтобы она принимала значение Char и возвращала значение Char в качестве вывода.

Live Demo

fType :: Char-> Char 
fType x = 'K' 
main = do  
   let x = 'v' 
   print (fType x) 

Приведенный выше фрагмент кода вызовет функцию fType () со значением char, равным «v», но возвращает другое значение char, то есть «K». Вот его вывод —

sh-4.3$ main 
'K'

Обратите внимание, что мы не собираемся использовать эти типы явно, потому что Haskell достаточно умен, чтобы перехватить тип до того, как он будет объявлен. В последующих главах этого урока мы увидим, как различные типы и классы типов делают язык Haskell строго типизированным.

Класс эквалайзера

Класс EQ type — это интерфейс, который предоставляет функциональные возможности для проверки равенства выражения. Любой класс Type, который хочет проверить равенство выражения, должен быть частью этого класса EQ Type.

Все упомянутые выше стандартные классы типа являются частью этого класса EQ . Всякий раз, когда мы проверяем какое-либо равенство, используя любой из типов, упомянутых выше, мы фактически вызываем класс EQ .

В следующем примере мы используем тип EQ для внутреннего использования операции «==» или «/ =».

Live Demo

main = do 
   if 8 /= 8 
      then putStrLn "The values are Equal" 
   else putStrLn "The values are not Equal"

Это даст следующий вывод —

sh-4.3$ main 
The values are not Equal 

Тип Орд Класс

Ord — это еще один интерфейсный класс, который дает нам функциональность упорядочения. Все типы, которые мы использовали до сих пор, являются частью этого интерфейса Ord . Как и интерфейс EQ, интерфейс Ord можно вызывать с помощью «>», «<», «<=», «> =», «сравнить».

Ниже приведен пример, где мы использовали «сравнить» функциональность этого класса типов.

Live Demo

main = print (4 <= 2) 

Здесь компилятор Haskell проверит, меньше ли 4 или равно 2. Так как это не так, код выдаст следующий вывод:

sh-4.3$ main 
False

Шоу

Функция Show имеет функцию печати аргумента в виде строки. Каким бы ни был его аргумент, он всегда печатает результат в виде строки. В следующем примере мы распечатаем весь список, используя этот интерфейс. «show» может использоваться для вызова этого интерфейса.

Live Demo

main = print (show [1..10]) 

Он выдаст следующий вывод на консоль. Здесь двойные кавычки указывают, что это значение типа String.

sh-4.3$ main 
"[1,2,3,4,5,6,7,8,9,10]" 

Читать

Интерфейс Read делает то же самое, что и Show, но не выводит результат в формате String. В следующем коде мы использовали интерфейс read для чтения строкового значения и преобразования его в значение Int.

Live Demo

main = print (readInt "12") 
readInt :: String -> Int 
readInt = read 

Здесь мы передаем переменную String («12») методу readInt, который, в свою очередь, возвращает 12 (значение Int) после преобразования. Вот его вывод —

sh-4.3$ main 
12

Enum

Enum — это другой тип класса Type, который включает последовательную или упорядоченную функциональность в Haskell. Этот класс Type может быть доступен с помощью таких команд, как Succ, Pred, Bool, Char и т. Д.

Следующий код показывает, как найти значение преемника 12.

Live Demo

main = print (succ 12) 

Это даст следующий результат —

sh-4.3$ main
13

ограниченный

Все типы, имеющие верхнюю и нижнюю границы, подпадают под этот класс типов. Например, данные типа Int имеют максимальную границу «9223372036854775807» и минимальную границу «-9223372036854775808».

Следующий код показывает, как Haskell определяет максимальную и минимальную границы типа Int.

Live Demo

main = do 
   print (maxBound :: Int) 
   print (minBound :: Int) 

Это даст следующий результат —

sh-4.3$ main
9223372036854775807
-9223372036854775808

Теперь попробуйте найти максимальную и минимальную границы типов Char, Float и Bool.

Num

Этот тип класса используется для числовых операций. Такие типы, как Int, Integer, Float и Double подпадают под этот класс Type. Посмотрите на следующий код —

Live Demo

main = do 
   print(2 :: Int)  
   print(2 :: Float) 

Это даст следующий результат —

sh-4.3$ main
2
2.0

интеграл

Интеграл можно рассматривать как подкласс класса Num Type. Класс Num Type содержит все типы чисел, тогда как класс Integral используется только для целых чисел. Int и Integer являются типами в этом классе типов.

плавучий

Как и Integral, Floating также является частью класса Num Type, но он содержит только числа с плавающей запятой. Следовательно, Float и Double подпадают под этот тип класса.

Класс пользовательского типа

Как и любой другой язык программирования, Haskell позволяет разработчикам определять пользовательские типы. В следующем примере мы создадим пользовательский тип и используем его.

Live Demo

data Area = Circle Float Float Float  
surface :: Area -> Float   
surface (Circle _ _ r) = pi * r ^ 2   
main = print (surface $ Circle 10 20 10 ) 

Здесь мы создали новый тип с именем Area . Далее мы используем этот тип для расчета площади круга. В приведенном выше примере «поверхность» — это функция, которая принимает Площадь в качестве входных данных и создает Float в качестве выходных данных.

Имейте в виду, что «данные» — это ключевое слово, и все пользовательские типы в Haskell всегда начинаются с заглавной буквы.

Это даст следующий результат —

sh-4.3$ main
314.15927

Haskell — Функции

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

  • Объявление функции состоит из имени функции и списка аргументов вместе с ее выводом.

  • Определение функции — это то, где вы на самом деле определяете функцию.

Объявление функции состоит из имени функции и списка аргументов вместе с ее выводом.

Определение функции — это то, где вы на самом деле определяете функцию.

Давайте возьмем небольшой пример функции add, чтобы детально понять эту концепцию.

Live Demo

add :: Integer -> Integer -> Integer   --function declaration 
add x y =  x + y                       --function definition 

main = do 
   putStrLn "The addition of the two numbers is:"  
   print(add 2 5)    --calling a function 

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

Как и большинство других языков, Haskell начинает компилировать код из метода main . Наш код сгенерирует следующий вывод —

The addition of the two numbers is:
7

Сопоставление с образцом

Сопоставление с образцом — это процесс сопоставления выражений определенного типа. Это всего лишь методика упрощения вашего кода. Эта техника может быть реализована в любом типе класса Type. If-Else может использоваться как альтернативный вариант сопоставления с образцом.

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

Посмотрите на следующий блок кода. Здесь мы использовали технику сопоставления с образцом для вычисления факториала числа.

Live Demo

fact :: Int -> Int 
fact 0 = 1 
fact n = n * fact ( n - 1 ) 

main = do 
   putStrLn "The factorial of 5 is:" 
   print (fact 5)

Мы все знаем, как рассчитать факториал числа. Компилятор начнет поиск функции с именем «fact» с аргументом. Если аргумент не равен 0, то число будет продолжать вызывать ту же функцию, на 1 меньше, чем у фактического аргумента.

Когда шаблон аргумента в точности совпадает с 0, он будет вызывать наш шаблон «факт 0 = 1». Наш код выдаст следующий результат:

The factorial of 5 is:
120

гвардия

Guards — концепция, очень похожая на сопоставление с образцом. При сопоставлении с образцом мы обычно сопоставляем одно или несколько выражений, но мы используем охранники для проверки какого-либо свойства выражения.

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

В следующем коде мы изменили нашу факториальную программу, используя концепцию охраны .

Live Demo

fact :: Integer -> Integer 
fact n | n == 0 = 1 
       | n /= 0 = n * fact (n-1) 
main = do 
   putStrLn "The factorial of 5 is:"  
   print (fact 5) 

Здесь мы объявили двух охранников , разделенных «|» и вызывая функцию факта из main . Внутри компилятор будет работать так же, как и в случае сопоставления с шаблоном, чтобы получить следующий вывод:

The factorial of 5 is:
120

Где пункт

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

Рассмотрим сценарий, в котором ваш ввод представляет собой сложное выражение с несколькими параметрами. В таких случаях вы можете разбить все выражение на маленькие части с помощью предложения where.

В следующем примере мы берем сложное математическое выражение. Мы покажем, как вы можете найти корни полиномиального уравнения [x ^ 2 — 8x + 6], используя Haskell.

Live Demo

roots :: (Float, Float, Float) -> (Float, Float)  
roots (a,b,c) = (x1, x2) where 
   x1 = e + sqrt d / (2 * a) 
   x2 = e - sqrt d / (2 * a) 
   d = b * b - 4 * a * c  
   e = - b / (2 * a)  
main = do 
   putStrLn "The roots of our Polynomial equation are:" 
   print (roots(1,-8,6))

Обратите внимание на сложность нашего выражения для вычисления корней данной полиномиальной функции. Это довольно сложно. Следовательно, мы разбиваем выражение, используя предложение where . Приведенный выше фрагмент кода сгенерирует следующий вывод:

The roots of our Polynomial equation are:
(7.1622777,0.8377223)

Функция рекурсии

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

Давайте снова рассмотрим наш пример сопоставления с образцом, где мы вычислили факториал числа. Поиск факториала числа является классическим случаем использования рекурсии. Здесь вы можете: «Чем сопоставление с образцом отличается от рекурсии?». Различие между этими двумя заключается в том, как они используются. Сопоставление с образцом работает при настройке ограничения терминала, тогда как рекурсия — это вызов функции.

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

Live Demo

fact :: Int -> Int 
fact 0 = 1 
fact n = n * fact ( n - 1 ) 

main = do 
   putStrLn "The factorial of 5 is:" 
   print (fact 5) 

Это даст следующий результат —

The factorial of 5 is:
120

Функция высшего порядка

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

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

Давайте возьмем пример, где мы будем импортировать встроенную карту функций более высокого порядка и использовать ее для реализации другой функции более высокого порядка в соответствии с нашим выбором.

Live Demo

import Data.Char  
import Prelude hiding (map) 

map :: (a -> b) -> [a] -> [b] 
map _ [] = [] 
map func (x : abc) = func x : map func abc  
main = print $ map toUpper "tutorialspoint.com" 

В приведенном выше примере мы использовали функцию toUpper класса Type Char для преобразования нашего ввода в верхний регистр. Здесь метод «map» принимает функцию в качестве аргумента и возвращает требуемый результат. Вот его вывод —

sh-4.3$ ghc -O2 --make *.hs -o main -threaded -rtsopts
sh-4.3$ main
"TUTORIALSPOINT.COM" 

Лямбда-выражение

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

Функция без определения называется лямбда-функцией. Лямбда-функция обозначается символом «\». Давайте возьмем следующий пример, где мы увеличим входное значение на 1 без создания какой-либо функции.

Live Demo

main = do 
   putStrLn "The successor of 4 is:"  
   print ((\x -> x + 1) 4)

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

Наше лямбда-выражение даст следующий результат:

sh-4.3$ main
The successor of 4 is:
5

Haskell — Подробнее о функциях

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

Функция головы

Головная функция работает в списке. Возвращает первый входной аргумент, который в основном является списком. В следующем примере мы передаем список с 10 значениями и генерируем первый элемент этого списка, используя функцию head .

Live Demo

 main = do 
    пусть х = [1..10]   
    putStrLn "Наш список:"  
    печать (х) 
    putStrLn "Первый элемент списка:" 
    печать (голова х)

Это даст следующий результат —

 Наш список: 
 [1,2,3,4,5,6,7,8,9,10]
 Первый элемент списка:
 1

Хвост Функция

Хвост — это функция, которая дополняет функцию головы . Он принимает список в качестве входных данных и выдает весь список без головной части. Это означает, что функция tail возвращает весь список без первого элемента. Взгляните на следующий пример —

Live Demo

 main = do 
    пусть х = [1..10]   
    putStrLn "Наш список:"  
    печать (х) 
    putStrLn "Хвост нашего списка:" 
    печать (хвост х) 

Это даст следующий результат —

 Наш список:
 [1,2,3,4,5,6,7,8,9,10]
 Хвост нашего списка:
 [2,3,4,5,6,7,8,9,10]

Последняя функция

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

Live Demo

 main = do 
    пусть х = [1..10]   
    putStrLn "Наш список:"  
    печать (х) 
    putStrLn "Последний элемент нашего списка:" 
    печать (последний х)

Это даст следующий результат —

 Наш список:
 [1,2,3,4,5,6,7,8,9,10]
 Последний элемент нашего списка:
 10

Функция инициализации

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

Live Demo

 main = do 
    пусть х = [1..10]   
    putStrLn "Наш список:"  
    печать (х) 
    putStrLn "Наш список без последней записи:"  
    печать (init x) 

Теперь понаблюдайте за его выводом —

 Наш список:
 [1,2,3,4,5,6,7,8,9,10]
 Наш список без последней записи:
 [1,2,3,4,5,6,7,8,9]

Нулевая функция

Null — это булева функция проверки, которая работает со строкой и возвращает True только тогда, когда данный список пуст, в противном случае возвращается False . Следующий код проверяет, является ли предоставленный список пустым или нет.

Live Demo

 main = do 
    пусть х = [1..10]   
    putStrLn "Наш список:"  
    печать (х) 
    putStrLn "Наш список пуст?"  
    печать (ноль х)

Это даст следующий результат —

 Наш список:
 [1,2,3,4,5,6,7,8,9,10]
 Наш список пуст?
 Ложь

Обратная функция

Он работает на входе String и преобразует весь вход в обратном порядке и дает один выход в результате. Ниже приведена база кода для этой функции.

Live Demo

 main = do 
    пусть х = [1..10]  
    putStrLn "Наш список:" 
    печать (х) 
    putStrLn "Список в обратном порядке:" 
    печать (обратный х)

Это даст следующий результат —

 Наш список:
 [1,2,3,4,5,6,7,8,9,10]
 Список в обратном порядке:
 [10,9,8,7,6,5,4,3,2,1]

Функция длины

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

Live Demo

 main = do 
    пусть х = [1..10]   
    putStrLn "Наш список:" 
    печать (х) 
    putStrLn "Длина этого списка:" 
    печать (длина х)

У нас есть 10 элементов в нашем списке, поэтому наш код выдаст 10 в качестве вывода.

 Наш список:
 [1,2,3,4,5,6,7,8,9,10]
 Длина этого списка:
 10

Взять функцию

Функция Take используется для создания подстроки из другой строки. Следующий код показывает, как вы можете использовать функцию take в Haskell —

Live Demo

 main = print (взять 5 ([1 .. 10])) 

Код генерирует подстроку, содержащую 5 элементов из предоставленного списка —

 [1,2,3,4,5]

Функция Drop

Эта функция также используется для генерации подстроки. Он действует как противоположность функции take . Посмотрите на следующий кусок кода —

Live Demo

 main = print (drop 5 ([1 .. 10])) 

Код удаляет первые 5 элементов из предоставленного списка и печатает оставшиеся 5 элементов. Это даст следующий результат —

 [6,7,8,9,10]

Максимальная функция

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

Live Demo

 main = do 
    пусть х = [1,45,565,1245,02,2]   
    putStrLn "Максимальное значение элемента списка:"  
    печать (максимум х)

Приведенный выше фрагмент кода сгенерирует следующий вывод:

 Максимальное значение элемента списка:
 1245

Минимальная функция

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

Live Demo

 main = do 
    пусть х = [1,45,565,1245,02,2]   
    putStrLn "Элемент минимального значения в списке:"  
    печать (минимум х)

Вывод вышеуказанного кода —

 Элемент минимального значения в списке:
 1

Функция суммы

Как следует из названия, эта функция возвращает суммирование всех элементов, представленных в предоставленном списке. Следующий код берет список из 5 элементов и возвращает их суммирование в качестве вывода.

Live Demo

 main = do 
    пусть х = [1..5] 
    putStrLn "Наш список:" 
    печать (х) 
    putStrLn "Суммирование элементов списка:" 
    печать (сумма х)

Это даст следующий результат —

 Наш список:
 [1,2,3,4,5]
 Суммирование элементов списка:
 15

Функция продукта

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

Live Demo

 main = do 
    пусть х = [1..5] 
    putStrLn "Наш список:" 
    печать (х) 
    putStrLn "Умножение элементов списка:" 
    печать (продукт х) 

Наш код выдаст следующий результат:

 Наш список:
 [1,2,3,4,5]
 Умножение элементов списка: 
 120

Элемент Функция

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

Следующий код проверяет, содержит ли предоставленный список элементов значение 786.

Live Demo

 main = do 
    пусть х = [1,45,155,1785] 
    putStrLn "Наш список:" 
    печать (х) 
    putStrLn "Содержит ли он 786?" 
    печать (элемент 786 (х))

Это даст следующий результат —

 Наш список:
 [1,45,155,1785]
 Содержит ли он 786?
 Ложь

Используйте тот же код, чтобы проверить, содержит ли предоставленный список значение 1785 или нет.

Haskell — Композиция функций

Композиция функций — это процесс использования выхода одной функции в качестве входа другой функции. Будет лучше, если мы изучим математику за композицией . В математике композиция обозначается через f {g (x)}, где g () — функция, а ее выход используется как вход другой функции, то есть f () .

Композиция функций может быть реализована с использованием любых двух функций, при условии, что тип вывода одной функции совпадает с типом ввода второй функции. Мы используем оператор точки (.) Для реализации композиции функций в Haskell.

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

Live Demo

eveno :: Int -> Bool 
noto  :: Bool -> String 

eveno x = if x `rem` 2 == 0 
   then True 
else False 
noto x = if x == True 
   then "This is an even Number" 
else "This is an ODD number" 

main = do 
   putStrLn "Example of Haskell Function composition" 
   print ((noto.eveno)(16))

Здесь, в основной функции, мы вызываем две функции, noto и eveno , одновременно. Сначала компилятор вызовет функцию eveno () с аргументом 16 . После этого компилятор будет использовать выходные данные метода eveno в качестве входных данных для метода noto () .

Его вывод будет следующим:

Example of Haskell Function composition                
"This is an even Number"

Поскольку мы поставляем число 16 в качестве входных данных (которое является четным числом), функция eveno () возвращает значение true , которое становится входным значением для функции noto () и возвращает вывод: «Это четное число».

Haskell — Модули

Если вы работали на Java, вы бы знали, как все классы связаны в папку с именем package . Точно так же Haskell можно рассматривать как набор модулей .

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

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

Модуль списка

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

В следующем примере мы использовали некоторые важные функции, доступные в модуле List.

Live Demo

import Data.List  

main = do  
   putStrLn("Different methods of List Module") 
   print(intersperse '.' "Tutorialspoint.com") 
   print(intercalate " " ["Lets","Start","with","Haskell"]) 
   print(splitAt 7 "HaskellTutorial") 
   print (sort [8,5,3,2,1,6,4,2])

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

Наш код даст следующий вывод —

Different methods of List Module
"T.u.t.o.r.i.a.l.s.p.o.i.n.t...c.o.m"
"Lets Start with Haskell"
("Haskell","Tutorial")
[1,2,2,3,4,5,6,8]

Чар модуль

Модуль Char имеет множество предопределенных функций для работы с типом символов. Посмотрите на следующий блок кода —

Live Demo

import Data.Char 

main = do  
   putStrLn("Different methods of Char Module") 
   print(toUpper 'a') 
   print(words "Let us study tonight") 
   print(toLower 'A')

Здесь функции toUpper и toLower уже определены внутри модуля Char . Это даст следующий результат —

Different methods of Char Module
'A'
["Let","us","study","tonight"]
'a'

Модуль карты

Карта — это несортированный тип данных с добавленной стоимостью. Это широко используемый модуль с множеством полезных функций. В следующем примере показано, как можно использовать предопределенную функцию, доступную в модуле «Карта».

Live Demo

import Data.Map (Map) 
import qualified Data.Map as Map  --required for GHCI  

myMap :: Integer -> Map Integer [Integer] 
myMap n = Map.fromList (map makePair [1..n]) 
   where makePair x = (x, [x])  

main = print(myMap 3)

Это даст следующий результат —

fromList [(1,[1]),(2,[2]),(3,[3])] 

Установить модуль

Модуль Set имеет несколько очень полезных предопределенных функций для манипулирования математическими данными. Набор реализован в виде двоичного дерева, поэтому все элементы в наборе должны быть уникальными.

Взгляните на следующий пример кода

Live Demo

import qualified Data.Set as Set   

text1 = "Hey buddy"   
text2 = "This tutorial is for Haskell"   

main = do  
   let set1 = Set.fromList text1   
       set2 = Set.fromList text2 
   print(set1) 
   print(set2)    

Здесь мы модифицируем строку в набор. Это даст следующий результат. Обратите внимание, что выходной набор не имеет повторения символов.

fromList " Hbdeuy"
fromList " HTaefhiklorstu"

Пользовательский модуль

Давайте посмотрим, как мы можем создать собственный модуль, который можно вызывать в других программах. Для реализации этого пользовательского модуля мы создадим отдельный файл с именем «custom.hs» вместе с нашим «main.hs» .

Давайте создадим пользовательский модуль и определим в нем несколько функций.

custom.hs

module Custom ( 
   showEven, 
   showBoolean 
) where 

showEven:: Int-> Bool 
showEven x = do 

if x 'rem' 2 == 0 
   then True 
else False 
showBoolean :: Bool->Int 
showBoolean c = do 

if c == True 
   then 1 
else 0 

Наш пользовательский модуль готов. Теперь давайте импортируем его в программу.

main.hs

import Custom 

main = do 
   print(showEven 4) 
   print(showBoolean True) 

Наш код сгенерирует следующий вывод —

True
1

Функция showEven возвращает True , поскольку «4» является четным числом. Функция showBoolean возвращает «1», поскольку булева функция, которую мы передали в функцию, — «True».

Haskell — вход и выход

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

Файлы и потоки

До сих пор мы жестко закодировали все входные данные в самой программе. Мы принимаем данные от статических переменных. Теперь давайте научимся читать и писать из внешнего файла.

Давайте создадим файл и назовем его «abc.txt». Затем введите следующие строки в этом текстовом файле: «Добро пожаловать в Tutorialspoint. Здесь вы получите лучший ресурс для изучения Haskell».

Далее мы напишем следующий код, который будет отображать содержимое этого файла на консоли. Здесь мы используем функцию readFile (), которая читает файл до тех пор, пока не найдет символ EOF.

main = do  
   let file = "abc.txt" 
   contents <- readFile file 
   putStrLn contents   

Приведенный выше фрагмент кода будет считывать файл «abc.txt» как строку, пока не встретит какой-либо символ конца файла. Этот кусок кода будет генерировать следующий вывод.

Welcome to Tutorialspoint
Here, you will get the best resource to learn Haskell.

Обратите внимание, что все, что печатается на терминале, записано в этом файле.

Аргумент командной строки

Haskell также предоставляет возможность управлять файлом через командную строку. Давайте вернемся к нашему терминалу и наберем «ghci» . Затем введите следующий набор команд —

let file = "abc.txt" 
writeFile file "I am just experimenting here." 
readFile file 

Здесь мы создали текстовый файл с именем «abc.txt». Далее мы вставили оператор в файл с помощью команды writeFile . Наконец, мы использовали команду readFile для печати содержимого файла на консоли. Наш код выдаст следующий результат:

I am just experimenting here.

Исключения

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

Если вы знакомы с Java, то вам может быть знаком блок Try-Catch, где мы обычно выдаем ошибку и отлавливаем ее в блоке catch . В Haskell у нас также есть та же функция для отлова ошибок во время выполнения.

Определение функции try выглядит так: «try :: Exception e => IO a -> IO (Either ea)». Взгляните на следующий пример кода. Он показывает, как вы можете поймать исключение «Разделить на ноль».

Live Demo

import Control.Exception 

main = do 
   result <- try (evaluate (5 `div` 0)) :: IO (Either SomeException Int) 
   case result of 
      Left ex   -> putStrLn $ "Caught exception: " ++ show ex 
      Right val -> putStrLn $ "The answer was: " ++ show val 

В приведенном выше примере мы использовали встроенную функцию try модуля Control.Exception , поэтому мы заранее перехватываем исключение. Выше кусок кода будет отображаться ниже вывода на экране.

Caught exception: divide by zero 

Haskell — Функтор

Функтор в Haskell является своего рода функциональным представлением различных типов, которые можно отобразить. Это концепция высокого уровня реализации полиморфизма. По словам разработчиков Haskell, все типы, такие как List, Map, Tree и т. Д., Являются экземпляром Haskell Functor.

Functor — это встроенный класс с определением функции, подобным —

class Functor f where 
   fmap :: (a -> b) -> f a -> f b 

Исходя из этого определения, мы можем заключить, что Functor является функцией, которая принимает функцию, скажем, fmap () и возвращает другую функцию. В приведенном выше примере fmap () является обобщенным представлением функции map () .

В следующем примере мы увидим, как работает Haskell Functor.

Live Demo

main = do  
   print(map (subtract 1) [2,4,8,16])      
   print(fmap (subtract 1) [2,4,8,16])   

Здесь мы использовали и map (), и fmap () над списком для операции вычитания. Вы можете заметить, что оба оператора приведут к одному и тому же результату списка, содержащего элементы [1,3,7,15].

Обе функции вызвали другую функцию subtract (), чтобы получить результат.

[1,3,7,15]
[1,3,7,15]

Тогда в чем разница между картой и fmap? Разница заключается в их использовании. Functor позволяет нам реализовать еще несколько функционалистов в разных типах данных, таких как «просто» и «ничего».

Live Demo

main = do 
   print (fmap  (+7)(Just 10)) 
   print (fmap  (+7) Nothing)

Приведенный выше фрагмент кода выдаст следующий вывод на терминал —

Just 17
Nothing

Аппликативный Функтор

Аппликативный функтор — это обычный функтор с некоторыми дополнительными функциями, предоставляемыми классом Applicative Type.

Используя Functor, мы обычно отображаем существующую функцию с другой функцией, определенной внутри нее. Но нет никакого способа сопоставить функцию, которая определена внутри Функтора, с другим Функтором. Вот почему у нас есть еще одно средство под названием Applicative Functor . Это средство отображения реализуется классом Applicative Type, определенным в модуле Control . Этот класс дает нам только два метода для работы: один — чистый, а другой — <*> .

Ниже приведено определение класса аппликативного функтора.

class (Functor f) => Applicative f where   
   pure :: a -> f a   
   (<*>) :: f (a -> b) -> f a -> f b   

В соответствии с реализацией мы можем отобразить другой Functor, используя два метода: «Pure» и «<*>» . Метод «Pure» должен принимать значение любого типа, и он всегда будет возвращать Applicative Functor этого значения.

В следующем примере показано, как работает Applicative Functor:

Live Demo

import Control.Applicative 

f1:: Int -> Int -> Int 
f1 x y = 2*x+y  
main = do  
   print(show $ f1 <$> (Just 1) <*> (Just 2) ) 

Здесь мы реализовали аппликативные функторы в вызове функции f1 . Наша программа даст следующий результат.

"Just 4"

Моноиды

Мы все знаем, что Хаскелл определяет все в форме функций. В функциях у нас есть опции для получения нашего ввода в качестве выхода функции. Вот что такое моноид .

Monoid — это набор функций и операторов, в которых выходные данные не зависят от его входных данных. Давайте возьмем функцию (*) и целое число (1). Теперь, каким бы ни был ввод, его вывод останется только тем же номером. То есть, если вы умножите число на 1, вы получите тот же номер.

Вот определение класса класса моноида.

class Monoid m where  
   mempty :: m 
   mappend :: m -> m -> m  
   mconcat :: [m] -> m 
   mconcat = foldr mappend mempty 

Посмотрите на следующий пример, чтобы понять использование Monoid в Haskell.

Live Demo

multi:: Int->Int 
multi x = x * 1 
add :: Int->Int 
add x = x + 0 

main = do  
   print(multi 9)  
   print (add 7)

Наш код выдаст следующий результат:

9
7

Здесь функция «multi» умножает ввод на «1». Точно так же функция «добавить» добавляет ввод с «0». В обоих случаях вывод будет таким же, как и ввод. Следовательно, функции {(*), 1} и {(+), 0} являются прекрасными примерами моноидов.

Хаскель — Монады

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

Все три правила строго применимы к объявлению Монады, которое выглядит следующим образом:

class Monad m where  
   return :: a -> m a 
   (>>=) :: m a -> (a -> m b) -> m b 
   (>>) :: m a -> m b -> m b 
   x >> y = x >>= \_ -> y 
   fail :: String -> m a  
   fail msg = error msg 

Три основных закона, которые применимы к декларации Монады:

  • Left Identity Law — функция возврата не меняет значение и не должна ничего менять в монаде. Это может быть выражено как «return> => mf = mf».

  • Правильный закон идентификации — функция возврата не меняет значение и не должна ничего менять в монаде. Это может быть выражено как «mf> => return = mf».

  • Ассоциативность — Согласно этому закону, и Functors, и экземпляр Monad должны работать одинаково. Это может быть математически выражено как «(f> ==> g)> => h = f> => (g> = h)».

Left Identity Law — функция возврата не меняет значение и не должна ничего менять в монаде. Это может быть выражено как «return> => mf = mf».

Правильный закон идентификации — функция возврата не меняет значение и не должна ничего менять в монаде. Это может быть выражено как «mf> => return = mf».

Ассоциативность — Согласно этому закону, и Functors, и экземпляр Monad должны работать одинаково. Это может быть математически выражено как «(f> ==> g)> => h = f> => (g> = h)».

Первые два закона повторяют одну и ту же точку, т. Е. Возврат должен иметь одинаковое поведение по обе стороны от оператора связывания .

Мы уже использовали много монад в наших предыдущих примерах, не осознавая, что они монады. Рассмотрим следующий пример, где мы используем монаду списка для генерации определенного списка.

Live Demo

main = do
   print([1..10] >>= (\x -> if odd x then [x*2] else []))

Этот код выдаст следующий вывод —

[2,6,10,14,18]

Haskell — молнии

Молния в Haskell — это, в основном, указатели, указывающие на определенное расположение структуры данных, такой как дерево .

Давайте рассмотрим дерево, имеющее 5 элементов [45,7,55,120,56], которые можно представить в виде идеального бинарного дерева. Если я хочу обновить последний элемент этого списка, то мне нужно пройти через все элементы, чтобы добраться до последнего элемента, прежде чем обновлять его. Правильно?

Но что, если бы мы могли построить наше дерево таким образом, чтобы дерево с N элементами было набором из [(N-1), N] . Тогда нам не нужно проходить через все нежелательные (N-1) элементы. Мы можем напрямую обновить N-й элемент. Это именно концепция молния. Он фокусируется или указывает на конкретное местоположение дерева, где мы можем обновить это значение, не пересекая все дерево.

В следующем примере мы реализовали концепцию Zipper в списке. Таким же образом можно реализовать Zipper в виде дерева или файловой структуры данных.

Live Demo

data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
type Zipper_List a = ([a],[a])    

go_Forward :: Zipper_List a -> Zipper_List a   
go_Forward (x:xs, bs) = (xs, x:bs)   
   
go_Back :: Zipper_List a -> Zipper_List a   
go_Back (xs, b:bs) = (b:xs, bs)    

main = do 
   let list_Ex = [1,2,3,4] 
   print(go_Forward (list_Ex,[]))       
   print(go_Back([4],[3,2,1])) 

Когда вы скомпилируете и запустите вышеуказанную программу, она выдаст следующий вывод:

([2,3,4],[1]) 
([3,4],[2,1])

Здесь мы фокусируемся на элементе всей строки при движении вперед или при движении назад.