На этом шаге мы рассмотрим данный способ определения функций.
Необходимость включения в парадигму функционального программирования понятия "локальная переменная" возникла в связи с тем, что иногда в процессе вычисления производятся лишние действия.
Например, для решения задачи может несколько раз потребоваться значение одной и той же функции, вычисленной при одних и тех же входных данных. В этом случае экономию времени и ресурсов во время вычислений позволят осуществить локальные функции, которые в функциональном программировании называются замыканиями (или частными определениями функций).
Существует два способа определения замыканий.
let <Имя_функции> = <Выражение>¦<Функция><Аргументы> in <Выражение_1>¦<Функция_1><Аргументы>
В объектах <Выражение_1> или <Аргументы> должно присутствовать имя локальной функции <Имя_функции>.
<Функция> where <Имя_функции_1> = <Выражение_1>¦<Функция_1><Аргументы> ... <Имя_функции_n> = <Выражение_n>¦<Функция_n><Аргументы>
После ключевого слова where должны быть объявлены функции, входящие в тело функции <Функция>.
Описанные способы определения локальных функций являются почти эквивалентными. Тем не менее:
<Выражение_1>¦<Функция_1><Аргументы> Рассмотрим простейший пример: ¦ Сравните с постфиксным способом: ¦ > (let x=y*y; y=5 in x/5)+5 ¦ z=x/5 10.0 ¦ where x = y*y y = 5
Приведем демонстрационные примеры:
-- Демонстрация нахождения вещественных корней квад- -- ратного уравнения с использованием различных син- -- таксических вариантов оформления замыкания: -- (1) where ... -- (2) let ... in --------------------------------------------------- twoRoots:: Float -> Float -> Float -> (Float,Float) twoRoots a b c = ( (-b-sqrt(b*b-4.0*a*c))/2.0/a, (-b+sqrt(b*b-4.0*a*c))/2.0/a ) ---------------------------------------------------- twoRoots1:: Float -> Float -> Float -> (Float,Float) twoRoots1 a b c = (d-e,d+e) where d = -b/(2.0*a) e = sqrt(b^2-4.0*a*c)/(2.0*a) ---------------------------------------------------- twoRoots2:: Float -> Float -> Float -> (Float,Float) twoRoots2 a b c = let d = -b/(2.0*a) e = sqrt(b^2-4.0*a*c)/(2.0*a) in (d-e,d+e) -- *************************** -- Неудачные тестовые примеры: ------------------------------------------ test = twoRoots 1 (-5) 6 == ( 2.0, 3.0) && twoRoots 1 5 6 == (-3.0,-2.0) && twoRoots1 1 (-5) 6 == ( 2.0, 3.0) && twoRoots 1 5 6 == (-3.0,-2.0) && twoRoots2 1 (-5) 6 == ( 2.0, 3.0) && twoRoots 1 5 6 == (-3.0,-2.0)
-- Демонстрация функции, определяющей вещественные кор- -- ни квадратного уравнения. -- Используются функции: -- ++ - операция конкатенации (соединения) строк; -- show f - функция, преобразующая значение функции f -- в тип String; -- fst - функция выделения первого компонента пары; -- snd - функция выделения второго компонента пары ------------------------------------------------------ roots:: Float -> Float -> Float -> String roots a b c | abs (discrim a b c)<=0.0001 = "X="++show (oneRoot a b c) | discrim a b c>0 = "X1=" ++ show f ++ ", X2=" ++ show s | True = "Действительных корней нет" ------------------------------------------ where discrim a b c = b^2-4.0*a*c oneRoot a b c = -b/(2.0*a) f = fst (twoRoots a b c) s = snd (twoRoots a b c) ------------------------------------------------ -- Функция возвращает пару, содержащую корни --------------------------------------------------- twoRoots:: Float -> Float -> Float -> (Float,Float) twoRoots a b c = (d-e,d+e) where d = -b/(2.0*a) e = sqrt(b^2-4.0*a*c)/(2.0*a) -- *************************************** -- Неудачные тестовые примеры: -------------------------------------- test = roots 1 (-8) 16 == "X=4.0" && roots 1 0 (-16) == "X1=-4.0, X2=4.0" && roots 4 2 1 == "Действительных корней нет"
-- Демонстрация функции, возвращающей вещественные корни -- квадратного уравнения, используя механизм where при -- описании локальных переменных. -- Используются: -- ++ - операция конкатенации (соединения) строк; -- show f - функция, преобразующая значение функции f -- в тип String ----------------------------------------- roots:: Float -> Float -> Float -> String roots a b c | abs (discrim a b c)<=0.00001 = "X=" ++ show (oneRoot a b c) | discrim a b c>0 = "X1=" ++ show f ++ ", X2=" ++ show s | True = "Вещественных корней нет" ---------------------------------------- where discrim a b c = b^2-4.0*a*c oneRoot a b c = -b/(2.0*a) f = fst (twoRoots a b c) s = snd (twoRoots a b c) ------------------------------------------------------- -- Функция возвращает два корня квадратного уравнения с -- помощью механизма let..in --------------------------------------------------- twoRoots:: Float -> Float -> Float -> (Float,Float) twoRoots a b c = let d = -b/(2.0*a) e = sqrt(b^2-4.0*a*c)/(2.0*a) in (d-e,d+e) -- *************************** -- Неудачные тестовые примеры: ------------------------------------- test = roots 1 (-8) 16 == "X=4.0" && roots 1 0 (-16) == "X1=-4.0, X2=4.0" && roots 4 2 1 == "Вещественных корней нет"
На следующем шаге мы рассмотрим двухмерный синтаксис.