Шаг 45.
Основы языка Haskell. Основные функции библиотеки Prelude. Определение функций с помощью замыкания (частных определений функций)

    На этом шаге мы рассмотрим данный способ определения функций.

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

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

    Существует два способа определения замыканий.

  1. Префиксный способ подразумевает объявление локальной функции <Имя_функции> до её использования в выражении или функции:
       let <Имя_функции> = <Выражение>¦<Функция><Аргументы>
       in <Выражение_1>¦<Функция_1><Аргументы>
    

        В объектах <Выражение_1> или <Аргументы> должно присутствовать имя локальной функции <Имя_функции>.

  2. Постфиксный способ подразумевает объявление локальной функции после её использования в некотором выражении:
       <Функция>
         where <Имя_функции_1> = <Выражение_1>¦<Функция_1><Аргументы>
                              ...
               <Имя_функции_n> = <Выражение_n>¦<Функция_n><Аргументы>
    

        После ключевого слова where должны быть объявлены функции, входящие в тело функции <Функция>.

    Описанные способы определения локальных функций являются почти эквивалентными. Тем не менее:

    Приведем демонстрационные примеры:

   -- Демонстрация нахождения вещественных корней квад-
   -- ратного уравнения с использованием различных син-
   -- таксических вариантов оформления замыкания:
   --   (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  == "Вещественных корней нет"
Файл с примерами можно взять здесь.

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




Предыдущий шаг Содержание Следующий шаг