HASKELL – язык функционального программирования
Поможем в ✍️ написании учебной работы
Поможем с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой

4.1. Типы

Программы на языке HASKELL представляют собой выражения, вычисление которых приводит к значениям. Каждое значение имеет тип. Для того, чтобы узнать тип некоторого выражения, можно использовать команду интерпретатора :type (или :t). Кроме того, можно выполнить команду :set +t, для того, чтобы интерпретатор автоматически печатал тип каждого вычисленного результата.

4.1.1. Основные типы

Основными типами языка HASKELL являются:

· Типы Integer и Int используется для представления целых чисел, причем значения типа Integer не ограничены по длине.

· Типы Float и Double используется для представления веществен­ных чисел.

· Тип Bool содержит два значения: True и False, и предназначен для представления результата логических выражений.

· Тип Char используется для представления символов.

Имена типов начинаются с заглавной буквы.

В большинстве случаев программист не обязан объявлять, каким типам принадлежат вводимые им переменные. Интерпретатор сам способен их вывести. Если же объявить тип необходимо, то используется конструкция вида: переменная::Тип. Если включена опция интерпретатора +t, он печатает значения в таком же формате.

Далее, текст, следующий за приглашением - вводимый пользователем, а следующий за этим текст - ответ системы.

Prelude>:set +t

Prelude>1

Integer

Prelude>1.2

Double

Prelude>’a’

’a’ :: Char

Prelude>True

True :: Bool

Благодаря развитой системе типов и строгой типизации многие ошибки в программе отслеживаются на этапе ее компиляции.

4.1.2. Арифметика

Интерпретатор можно использовать для вычисления арифметических выражений. При этом можно использовать операторы +, -, *, /, ^ (сложение, вычитание, умножение и деление, возведение в степень, стандартные математические функции sqrt, sin, cos, exp и.т.д). Сеанс работы может выглядеть следующим образом:

Prelude>2*2

Integer

Prelude>4*5 + 1

Integer

Prelude>2^3

Integer

При вызове функции не обязательно помещать аргумент в скобки. Можно писать sqrt 2, а не sqrt(2). Пример:

Prelude>sqrt 2

Double

Prelude>1 + sqrt 2

Double

Prelude>sqrt 2 + 1

Double

Prelude>sqrt (2 + 1)

Double

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

Целочисленные выражения вычисляются с неограниченным числом разрядов, тип Integer может хранить целые числа произвольной длины.

4.1.3. Кортежи

Помимо перечисленных выше простых типов, можно определять значения составных типов. Например, для задания точки на плоскости необходимы два числа, соответствующие ее координатам. В языке HASKELL пару можно задать, перечислив компоненты через запятую и взяв их в скобки: (5,3). Компоненты пары не обязательно должны принадлежать одному типу.

В общем случае, если a и b — некоторые произвольные типы, тип пары, в которой первый элемент принадлежит типу a, а второй — типу b, обозначается как (a,b). Например, пара (5,3) имеет тип (Integer, Integer); пара (1, ’a’) принадлежит типу (Integer, Char), пара ((1,’a’),1.2) принадлежит типу ((Integer,Char),Double).

Конструкции вида (1,2) и (Integer,Integer) выглядят похоже, но обозначают совершенно разные сущности. Первая является значением, вторая — типом.

Для работы с парами используются стандартные функции fst и snd, возвращающие, соответственно, первый и второй элементы пары. Их можно использовать следующим образом

Prelude>fst (5, True)

Integer

Prelude>snd (5, True)

True :: Bool

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

Prelude>(1,2,3)

(1,2,3) :: (Integer,Integer,Integer)

Prelude>(1,2,3,4)

(1,2,3,4) :: (Integer,Integer,Integer,Integer)

Такие структуры данных называются кортежами. В кортеже может храниться фиксированное количество разнородных данных. Функции fst и snd определены только для пар и не работают для других кортежей.

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

Prelude>fst (snd (1, (’a’, 23.12)))

’a’ :: Integer

4.1.4. Списки

В отличие от кортежей, списокможет хранить произвольное количество элементов. Чтобы задать список, необходимо в квадратных скобках перечислить его элементы через запятую. Все эти элементы должны принадлежать одному и тому же типу. Тип списка с элементами, принадлежащими типу a, обозначается как [a].

Prelude>[1,2]

[1,2] :: [Integer]

Prelude>[’1’,’2’,’3’]

[’1’,’2’,’3’] :: [Char]

Пустой список обозначается как [].

Оператор «:» (двоеточие) используется для добавления элемента в начало списка. Его левым аргументом должен быть элемент (голова), а правым — список (хвост):

Prelude>1:[2,3]

[1,2,3] :: [Integer]

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

[’5’,’1’,’2’,’3’,’4’,’5’] :: [Char]

Prelude>False:[]

[False] :: [Bool]

С помощью оператора «:» и пустого списка можно построить любой список:

Prelude>1:(2:(3:[]))

[1,2,3] :: [Integer]

Оператор «:» ассоциативен вправо, поэтому в приведенном выше выражении можно опустить скобки:

Prelude>1:2:3:[]

[1,2,3] :: [Integer]

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

Prelude>[(1,’a’),(2,’b’)]

[(1,’a’),(2,’b’)] :: [(Integer,Char)]

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

[[1,2],[3,4,5]] :: [[Integer]]

Для работы со списками существует большое количество функций. Рассмотрим некоторые из них.

• Функция head возвращает первый элемент списка.

• Функция tail возвращает список без первого элемента.

• Функция length возвращает длину списка.

Функции head и tail определены для непустых списков.

Prelude>head [1,2,3]

Integer

Prelude>tail [1,2,3]

[2,3] :: [Integer]

Prelude>tail [1]

[] :: [Integer]

Prelude>length [1,2,3]

Int

Для конкатенации списков определен оператор ++.

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

[1,2,3,4] :: [Integer]

4.1.5. Строки

Строковые значения задаются в двойных кавычках. Они принадлежат типу String.

Prelude>"hello"

"hello" :: String

В действительности строки являются списками символов; а тип String является синонимом для [Char]. Все функции для работы со списками можно использовать при работе со строками:

Prelude>head "hello"

’h’ :: Char

Prelude>tail "hello"

"ello" :: [Char]

Prelude>length "hello"

Int

Prelude>"hello" ++ ", world"

"hello, world" :: [Char]

Для преобразования числовых значений в строки и наоборот существуют функции read и show:

Prelude>show 1

"1" :: [Char]

Prelude>"Formula " ++ show 1

"Formula 1" :: [Char]

Prelude>1 + read "12"

Integer

Если функция show не сможет преобразовать строку в число, она сообщит об ошибке.

4.2. Определение функций

Определения пользовательских функций должны находиться в файле, который нужно загрузить в интерпретатор. Для редактирования программы можно использовать внешний редактор (например, Блокнот).

Введем в файл текст программы:

x = [1,2,3]

Сохраним файл и загрузим его в интерпретатор. При загрузке программа исполнится. Теперь значение переменной x будет определено:

Main>x

[1,2,3] :: [Integer]

Запишем в файл следующий текст:

square :: Integer -> Integer

square x= x* x

Первая строка объявляет тип функции square. Вторая – определяет, что функция square возвращает квадрат своего аргумента.

Функции в языке HASKELL являются равноправными с такими значениями, как целые и вещественные числа, символы, строки, списки и т.д. Функции можно передавать в качестве аргументов в другие функции, возвращать их из функций и т.п. Как и все значения в языке HASKELL, функции имеют тип. Тип функции, принимающей значения типа a и возвращающей значения типа b, обозначается как a->b.

Загрузим созданный файл и выполним следующие команды:

Main>:type square

square :: Integer -> Integer

Main>square 2

Integer

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

Имена определяемых пользователем функций и переменных должны начинаться с латинской буквы в нижнем регистре. Остальные символы в имени могут быть прописными или строчными латинскими буквами, цифрами или символами _ и (подчеркивание и апостроф). Ниже перечислены примеры правильных имен переменных:

var var1 variableName variable_name var’

В языке HASKELL определены два вида комментариев: строчные и блочные. Строчный комментарий начинается с символов – и продолжается до конца строки. Блочный комментарий начинается символами {- и продолжается до символов -}. Разумеется, комментарии игнорируется интерпретатором или компилятором. Пример:

f x = x -- Строчный комментарий

g x y = {- Блочный комментарий.

С продолжением. -} x+y

4.2.1. Условные выражения

Запишем функцию signum, вычисляющую знак числа:

signum :: Integer -> Integer

signum x= if x >0 then 1 else if x < 0 then -1 else 0

В условном выражении должны присутствовать и then-часть и else-часть. Выражения в then-части и в else-части условного оператора должны принадлежать одному типу.

Условие в определении условного оператора представляет собой любое выражение типа Bool. Например, сравнения. При сравнении можно использовать следующие операторы: <, >, <=, >= == ,/= (меньше, больше, меньше или равно, больше или равно, равно, неравно).

Выражения типа Bool можно комбинировать с помощью логических операторов && и || (И и ИЛИ), и функции отрицания not. Примеры допустимых условий:

x >= 0 && x <= 10,

x >3 &&x /= 10,

(x > 10 || x< -10) && not(x == y).

Разумеется, можно определять свои функции, возвращающие значения типа Bool, и использовать их в качестве условий. Например, можно определить функцию isPositive, возвращающую True, если ее аргумент неотрицателен и False в противном случае:

isPositive :: Integer -> Bool

isPositive x = if x > 0 then True else False

Теперь функцию signum можно определить следующим образом:

signum x = if isPositive x then 1 else if x < 0 then -1 else 0

Функцию isPositive можно определить проще:

isPositive x = x > 0

4.2.2. Функции многих переменных и порядок определения функций

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

add :: Integer -> Integer -> Integer

add x y = x + y

Считается, что операция -> ассоциативна вправо. Таким образом, тип функции add может быть прочитан как Integer -> (Integer -> Integer), т.е. в соответствие с правилом карринга, результатом применения функции add к одному аргументу будет функция типа Integer -> Integer. Вообще, тип функции с n аргументами типов t1, t2, ..., tn, и результатом типа a, записывается в виде t1->t2->...->tn->a.

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

factorial :: Integer -> Integer

factorial n = if n == 0 then 1 else n * factorial (n -1)

Дата: 2016-10-02, просмотров: 242.