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» на консоли.
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. Возможно, вам придется обновить конфигурацию CABAL вашей системы.
Настройка среды в MAC
Чтобы настроить среду Haskell в своей системе MAC, перейдите на их официальный веб-сайт https://www.haskell.org/platform/mac.html и загрузите установщик Mac.
Настройка среды в Linux
Установка Haskell в системе на основе Linux требует запуска некоторой команды, которая не так проста, как MAC и Windows. Да, это утомительно, но это надежно.
Вы можете следовать инструкциям ниже, чтобы установить Haskell в вашей системе Linux —
Шаг 1 — Чтобы настроить среду Haskell в вашей системе Linux, перейдите на официальный сайт https://www.haskell.org/platform/linux.html и выберите свой дистрибутив. Вы найдете следующий экран в вашем браузере.
Шаг 2 — Выберите ваш дистрибутив. В нашем случае мы используем Ubuntu. Выбрав эту опцию, вы получите следующую страницу на вашем экране с командой для установки Haskell в нашей локальной системе.
Шаг 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 —
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 —
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 с помощью оператора умножения —
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 —
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 —
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 () будет вести себя в соответствии с определенным типом.
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 , давайте изменим приведенный выше код следующим образом:
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» и наблюдаем разницу.
fType :: Integer -> Integer -> Integer fType x y = x*x + y*y main = print (fType 212124454 4454545445455454545445445454544545)
Теперь он выдаст следующий результат:
sh-4.3$ main 1984297512562793395882644631364297686099210302577374055141
терка
Посмотрите на следующий фрагмент кода. Он показывает, как работает тип Float в Haskell —
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 — число с плавающей запятой с двойной точностью в конце. Взгляните на следующий пример —
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:
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 в качестве вывода.
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 для внутреннего использования операции «==» или «/ =».
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 можно вызывать с помощью «>», «<», «<=», «> =», «сравнить».
Ниже приведен пример, где мы использовали «сравнить» функциональность этого класса типов.
main = print (4 <= 2)
Здесь компилятор Haskell проверит, меньше ли 4 или равно 2. Так как это не так, код выдаст следующий вывод:
sh-4.3$ main False
Шоу
Функция Show имеет функцию печати аргумента в виде строки. Каким бы ни был его аргумент, он всегда печатает результат в виде строки. В следующем примере мы распечатаем весь список, используя этот интерфейс. «show» может использоваться для вызова этого интерфейса.
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.
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.
main = print (succ 12)
Это даст следующий результат —
sh-4.3$ main 13
ограниченный
Все типы, имеющие верхнюю и нижнюю границы, подпадают под этот класс типов. Например, данные типа Int имеют максимальную границу «9223372036854775807» и минимальную границу «-9223372036854775808».
Следующий код показывает, как Haskell определяет максимальную и минимальную границы типа Int.
main = do print (maxBound :: Int) print (minBound :: Int)
Это даст следующий результат —
sh-4.3$ main 9223372036854775807 -9223372036854775808
Теперь попробуйте найти максимальную и минимальную границы типов Char, Float и Bool.
Num
Этот тип класса используется для числовых операций. Такие типы, как Int, Integer, Float и Double подпадают под этот класс Type. Посмотрите на следующий код —
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 позволяет разработчикам определять пользовательские типы. В следующем примере мы создадим пользовательский тип и используем его.
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, чтобы детально понять эту концепцию.
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 может использоваться как альтернативный вариант сопоставления с образцом.
Сопоставление с образцом можно рассматривать как вариант динамического полиморфизма, когда во время выполнения могут выполняться разные методы в зависимости от их списка аргументов.
Посмотрите на следующий блок кода. Здесь мы использовали технику сопоставления с образцом для вычисления факториала числа.
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, но они функционально отличаются.
В следующем коде мы изменили нашу факториальную программу, используя концепцию охраны .
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.
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.
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, то вы обнаружите, что большинство библиотечных функций написаны в более высоком порядке.
Давайте возьмем пример, где мы будем импортировать встроенную карту функций более высокого порядка и использовать ее для реализации другой функции более высокого порядка в соответствии с нашим выбором.
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 без создания какой-либо функции.
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 .
main = do пусть х = [1..10] putStrLn "Наш список:" печать (х) putStrLn "Первый элемент списка:" печать (голова х)
Это даст следующий результат —
Наш список: [1,2,3,4,5,6,7,8,9,10] Первый элемент списка: 1
Хвост Функция
Хвост — это функция, которая дополняет функцию головы . Он принимает список в качестве входных данных и выдает весь список без головной части. Это означает, что функция tail возвращает весь список без первого элемента. Взгляните на следующий пример —
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]
Последняя функция
Как следует из названия, он возвращает последний элемент списка, который предоставляется в качестве входных данных. Проверьте следующий пример.
main = do пусть х = [1..10] putStrLn "Наш список:" печать (х) putStrLn "Последний элемент нашего списка:" печать (последний х)
Это даст следующий результат —
Наш список: [1,2,3,4,5,6,7,8,9,10] Последний элемент нашего списка: 10
Функция инициализации
Init работает точно так же, как функция tail . Он принимает список в качестве аргумента и возвращает весь список без последней записи.
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 . Следующий код проверяет, является ли предоставленный список пустым или нет.
main = do пусть х = [1..10] putStrLn "Наш список:" печать (х) putStrLn "Наш список пуст?" печать (ноль х)
Это даст следующий результат —
Наш список: [1,2,3,4,5,6,7,8,9,10] Наш список пуст? Ложь
Обратная функция
Он работает на входе String и преобразует весь вход в обратном порядке и дает один выход в результате. Ниже приведена база кода для этой функции.
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]
Функция длины
Эта функция используется для вычисления длины списка, заданного в качестве аргумента. Взгляните на следующий пример —
main = do пусть х = [1..10] putStrLn "Наш список:" печать (х) putStrLn "Длина этого списка:" печать (длина х)
У нас есть 10 элементов в нашем списке, поэтому наш код выдаст 10 в качестве вывода.
Наш список: [1,2,3,4,5,6,7,8,9,10] Длина этого списка: 10
Взять функцию
Функция Take используется для создания подстроки из другой строки. Следующий код показывает, как вы можете использовать функцию take в Haskell —
main = print (взять 5 ([1 .. 10]))
Код генерирует подстроку, содержащую 5 элементов из предоставленного списка —
[1,2,3,4,5]
Функция Drop
Эта функция также используется для генерации подстроки. Он действует как противоположность функции take . Посмотрите на следующий кусок кода —
main = print (drop 5 ([1 .. 10]))
Код удаляет первые 5 элементов из предоставленного списка и печатает оставшиеся 5 элементов. Это даст следующий результат —
[6,7,8,9,10]
Максимальная функция
Эта функция используется для поиска элемента с максимальным значением из предоставленного списка. Давайте посмотрим, как использовать это на практике —
main = do пусть х = [1,45,565,1245,02,2] putStrLn "Максимальное значение элемента списка:" печать (максимум х)
Приведенный выше фрагмент кода сгенерирует следующий вывод:
Максимальное значение элемента списка: 1245
Минимальная функция
Эта функция используется для поиска элемента с минимальным значением из предоставленного списка. Это просто противоположность максимальной функции.
main = do пусть х = [1,45,565,1245,02,2] putStrLn "Элемент минимального значения в списке:" печать (минимум х)
Вывод вышеуказанного кода —
Элемент минимального значения в списке: 1
Функция суммы
Как следует из названия, эта функция возвращает суммирование всех элементов, представленных в предоставленном списке. Следующий код берет список из 5 элементов и возвращает их суммирование в качестве вывода.
main = do пусть х = [1..5] putStrLn "Наш список:" печать (х) putStrLn "Суммирование элементов списка:" печать (сумма х)
Это даст следующий результат —
Наш список: [1,2,3,4,5] Суммирование элементов списка: 15
Функция продукта
Вы можете использовать эту функцию, чтобы умножить все элементы в списке и распечатать его значение.
main = do пусть х = [1..5] putStrLn "Наш список:" печать (х) putStrLn "Умножение элементов списка:" печать (продукт х)
Наш код выдаст следующий результат:
Наш список: [1,2,3,4,5] Умножение элементов списка: 120
Элемент Функция
Эта функция используется для проверки того, содержит ли предоставленный список определенный элемент или нет. Соответственно, он либо возвращает истину, либо ложь .
Следующий код проверяет, содержит ли предоставленный список элементов значение 786.
main = do пусть х = [1,45,155,1785] putStrLn "Наш список:" печать (х) putStrLn "Содержит ли он 786?" печать (элемент 786 (х))
Это даст следующий результат —
Наш список: [1,45,155,1785] Содержит ли он 786? Ложь
Используйте тот же код, чтобы проверить, содержит ли предоставленный список значение 1785 или нет.
Haskell — Композиция функций
Композиция функций — это процесс использования выхода одной функции в качестве входа другой функции. Будет лучше, если мы изучим математику за композицией . В математике композиция обозначается через f {g (x)}, где g () — функция, а ее выход используется как вход другой функции, то есть f () .
Композиция функций может быть реализована с использованием любых двух функций, при условии, что тип вывода одной функции совпадает с типом ввода второй функции. Мы используем оператор точки (.) Для реализации композиции функций в Haskell.
Взгляните на следующий пример кода. Здесь мы использовали состав функции, чтобы вычислить, является ли входное число четным или нечетным.
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.
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 имеет множество предопределенных функций для работы с типом символов. Посмотрите на следующий блок кода —
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'
Модуль карты
Карта — это несортированный тип данных с добавленной стоимостью. Это широко используемый модуль с множеством полезных функций. В следующем примере показано, как можно использовать предопределенную функцию, доступную в модуле «Карта».
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 имеет несколько очень полезных предопределенных функций для манипулирования математическими данными. Набор реализован в виде двоичного дерева, поэтому все элементы в наборе должны быть уникальными.
Взгляните на следующий пример кода
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)». Взгляните на следующий пример кода. Он показывает, как вы можете поймать исключение «Разделить на ноль».
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.
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 позволяет нам реализовать еще несколько функционалистов в разных типах данных, таких как «просто» и «ничего».
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:
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.
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)».
Первые два закона повторяют одну и ту же точку, т. Е. Возврат должен иметь одинаковое поведение по обе стороны от оператора связывания .
Мы уже использовали много монад в наших предыдущих примерах, не осознавая, что они монады. Рассмотрим следующий пример, где мы используем монаду списка для генерации определенного списка.
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 в виде дерева или файловой структуры данных.
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])
Здесь мы фокусируемся на элементе всей строки при движении вперед или при движении назад.