Учебники

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 в качестве аргумента и печатает выходное значение. Мы в основном работаем с одной функцией, даже не объявляя ее должным образом. В этом красота лямбда-выражений.

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