Шаг 118.
Основы языка Haskell. Потенциально бесконечные списки. Конструирование списков с помощью определителя списка

    На этом шаге мы рассмотрим данный способ создания списков.

    Определитель списка является "синтаксическим сахаром" и используется для моделирования математического понятия "множество", определяемого с помощью характеристической функции множества (характеристического предиката).

    Напомним, что синтаксический сахар - это особенности языка, которые упрощают чтение и написание кода и дают возможность выразить одни и те же действия разными способами.

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

    Синтаксис определителя списка таков (читается: "список элементов x, взятых из исходного списка xs"):

   [x¦x <- xs]

    Слово x <- xs называется генератором списка; генератор для каждого x должен быть определён однозначно.

    Генерация элементов списка происходит в соответствии с предикатом:

   <Имя_функции> = [x¦x <- xs, <Условное_выражение_1>,
                               <Условное_выражение_2>,
                                        ...
                               <Условное_выражение_n>]

    Условия, составляющие предикат определителя списка, можно представить в отдельной функции:

   <Имя_функции> = [x¦x <- xs, <Имя_функции-предиката>]
       where <Имя_функции-предиката> = <Условное_выражение>

    Приведем ряд демонстрационных примеров.

   -- Демонстрация функции, конструирующей список из
   -- отрицательных элементов числового списка xs 
   ----------------------------------------------
   negLst:: [Int] -> [Int]
   negLst xs = [x|x <- xs, x<0]
   -------------------------------------------------
   -- Демонстрация функции, конструирующей список из
   -- чётных элементов числового списка xs
   ---------------------------------------
   evenLst:: [Int] -> [Int]
   evenLst xs = [x|x <- xs, x `mod` 2==0]
   --------------------------------------------------
   -- Демонстрация функции, конструирующей список пар
   -- элементов исходных двух списков  xs  и  ys, при
   -- этом первый элемент пары должен быть не  меньше
   -- второго элемента
   ------------------------------------
   para:: [Int] -> [Int] -> [(Int,Int)]
   para xs ys = [(x,y)| x <- xs, y <- ys, x>=y]
   ------------------------------------------------
   -- Демонстрация функций, конструирующих конечные
   -- n-элементные списки:
   --  (1) натуральных чисел;
   --  (2) нечётных натуральных чисел;
   --  (3) квадратов натуральных чисел;
   --  (4) факториалов;
   --  (5) степеней числа 2
   ------------------------
   f1 n = [1..n]
   f2 n = [1,3..n]
   f3 n = map (^2) [1..n]
   ------------------------
   f4 n = map (fact) [1..n]
       where fact 0 = 1
             fact x = x*fact (x-1)
   -------------------------------
   f5 n = map (2^) [1..n]
   ------------------------------
   -- Неудачные тестовые примеры:
   -----------------------------------------
   test1 =   negLst []                 == []
          && negLst [-4]               == [-4]
          && negLst [4]                == []
          && negLst [-1,2,-3]          == [-1,-3]
          && negLst [1,2,3,4,5]        == []
          && negLst [-1,-2,-3,-4]      == [-1,-2,-3,-4]
          && negLst [1,-2,3,-4,-5,6,7] == [-2,-4,-5]
   -------------------------------------------------
   test2 =   evenLst []                        == []
          && evenLst [1]                       == []
          && evenLst [1,2]                     == [2]
          && evenLst [1,2,-3,-4,5,6]           == [2,-4,6]
          && evenLst [10,22,314,411,523,61111] == [10,22,314]
          && evenLst [-11,314,411,-522,61111]  == [314,-522]
   --------------------------------------------------------------
   test3 =   para [3] [-1,0,-3,5]        == [(3,-1),(3,0),(3,-3)]
          && para [3,4,5] [6,78]         == []
          && para [1] []                 == []
          && para [] [2]                 == []
          && para [2] [1]                == [(2,1)]
          && para [-3,-4,5] [-6,-2,78,4] == [(-3,-6),(-4,-6),
                                             (5,-6),(5,-2),(5,4)]
   --------------------------------------------------------------
   test4 =   f1 5 == [1,2,3,4,5]
          && f2 5 == [1,3,5]
          && f3 5 == [1,4,9,16,25]
          && f4 5 == [1,2,6,24,120]
          && f5 5 == [2,4,8,16,32]
Файл с примерами можно взять здесь.
   -- Демонстрация вычисления значения функции y=cos(x)
   -- с помощью разложения функции в ряд Тейлора в ок-
   -- рестности точки x=0
   --
   --             2    4    6              2k
   --            x    x    x           k  x
   --  cos(x)=1- -- + -- - -- +...+(-1)  ----- +...
   --            2!   4!   6!            (2k)!
   --
   -- *********************************************
   -- Результат вычисления значения функции cos(x);
   -- k - количество слагаемых ряда Тейлора
   ----------------------------------------
   cos' x k = sum (abc x k)
   -- ****************************************
   -- Функция, возвращающая список, содержащий
   -- k слагаемых ряда Тейлора
   -----------------------------------
   abc:: Double -> Integer -> [Double]
   abc x k = [a x n | n <- [0..k]]
   -- *****************************************
   -- Функция, возвращающая значение n-го члена
   -- ряда Тейлора в точке x
   -------------------------------
   a:: Double -> Integer -> Double
   a x n | n==0 = 1
         | True = -x*x/((2*y-1)*2*y)*(a x (n-1))
       where y = fromInteger n 
   -- ******************************************
   -- Функция, возвращающая "остаток" от деления
   -- вещественных чисел a и b 
   ----------------------------------
   perev:: Double -> Double -> Double
   perev a b = a-b*fromInt (truncate (a/b))
   -- *************************************
   -- Функция, реализующая тестирование
   --------------------------------------------
   test :: Double -> Integer ->(Double, Double) 
   test x k = (cos' (perev x (2*pi)) k, cos x)
   -------------------------------------------
Файл с примерами можно взять здесь.
   -- Демонстрация вычисления значения функции y=sin(x)
   -- с помощью разложения функции в ряд Тейлора в ок-
   -- рестности точки x=0
   --
   --             3    5    7              2k+1
   --            x    x    x           k  x
   --  sin(x)=x- -- + -- - -- +...+(-1)  ------- +...
   --            3!   5!   7!            (2k+1)!
   --
   -- *********************************************
   -- Результат вычисления значения функции sin(x);
   -- k - количество слагаемых ряда Тейлора
   ----------------------------------------
   sin' x k = sum (abc x k)
   -- ****************************************
   -- Функция, возвращающая список, содержащий
   -- k слагаемых ряда Тейлора
   -----------------------------------
   abc:: Double -> Integer -> [Double]
   abc x k = [a x n | n <- [0..k]]
   -- *****************************************
   -- Функция, возвращающая значение n-го члена
   -- ряда Тейлора в точке x
   -------------------------------
   a:: Double -> Integer -> Double
   a x k | k==0 = x
         | True = -x*x/((2*y+1)*2*y)*(a x (k-1))
       where y = fromInteger k 
   -- ******************************************
   -- Функция, возвращающая "остаток" от деления
   -- вещественных чисел a и b 
   ----------------------------------
   perev:: Double -> Double -> Double
   perev a b = a-b*fromInt (truncate (a/b))
   -- *************************************
   -- Функция, реализующая тестирование
   --------------------------------------------
   test :: Double -> Integer -> (Double,Double) 
   test x k = (sin' (perev x (2*pi)) k, sin x)
Файл с примерами можно взять здесь.


   Замечания.
  1. Иногда полезно воспользоваться следующими утверждениями:
       [f x|x <- lst] = map f lst,
    
       [x|x <- lst, p x] = filter p lst,
    
       concat [[e|p]|Q] = [e|Q, p]
    
  2. В языке программирования Python определитель списка называется списковым включением . 0 (от англ. comprehension - понимание, включение, охват).

    На следующем шаге мы приведем перечень задач для самостоятельного решения.




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