Standart ML. Урок 10. Определения функций

Standart ML   27 октября 2013  Автор статьи:  
Немного о функциях в Standart ML
  • Новые функции определяются посредством привязки к функциональным значениям.
  • Функции используются путем применения их к аргументам (отсюда термин аппликация).
  • Функция является обычным значением, следовательно можно привязать идентификатор к функции:

    expSize

  • Функции имеют тип.

    - size;
    > val it = fn : string -> int
  • Функциональные типы
    • Функциональные типы — это составные типы, членами которых являются функции.
    • Запись функциональных типов: α -> β, где α — тип области определения функции, а β — тип области значений.
    • Выражение функционального типа имеет в качестве значения функцию, которая может быть применена к аргументу типа α (и только к аргументу такого типа). При успешном завершении вычисления возвращается результат типа β.

    [important]Аппликация e e0 допустима тогда и только тогда, когда тип e (size) есть α -> β (string -> int), тип e0 («функция size подсчитывает количество символов в строке») — α, а тип всего выражения есть β.[/important]

    Рассмотрим аппликации e e0, где выражением e являются идентификаторы (size, not, round):


  • - size;
    > val it = fn : string -> int
    - size "функция size подсчитывает количество символов в строке";
    > val it = 54 : int

  • - not;
    > val it = fn : bool -> bool
    - not false;
    > val it = true : bool

  • - round;
    > val it = fn : real -> int
    - round 45.90;
    > val it = 46 : int
  • Конструкция fun
    • Определение функции осуществляется с помощью конструкции fun — конструкция fun устанавливает привязку идентификатора к функциональному значению.
    • Общий формат конструкции:
      - fun nameFunction argument = argument * argument + 2;
      где (argument * argument + 2) — тело функции.
    • Описание конструкции:
      • ключевое слово fun;
      • имя функции;
      • аргументы функции, которые задаются образцом;
      • знак равенства;
      • тело функции.
    • В ходе применения и выполнения функции, значение аргумента сопоставляется с параметром-образцом аналогично при привязке к значению. Результатом сопоставления является некоторая среда, в которой и выполняется вычисление тела функции.

    Примеры определения функций:

  • Функция square возводит x в квадрат.


    - fun square x = x * x;
    > val square = fn : int -> int
    - square 4;
    > val it = 16 : int

  • Функция sum складывает x и y. При использовании функции (sum) нескольких аргументов (x, y), аргументы функции записываются в виде упорядоченной (ых) энки (энок).


    - fun sum (x, y) = x + y;
    > val sum = fn : int * int -> int
    - sum (14, 16 div 3);
    > val it = 19 : int

  • Функция mul соединяет две строки. Заметим, что по умолчанию конструктор функции fun имеет целочисленный тип int. Чтобы задать тип функции, следует воспользоваться приведенной ниже конструкцией (mul (x, y) : string):


    - fun mul (x, y) : string = x ^ y;
    > val mul = fn : string * string -> string
    - mul ("бурати", "но");
    > val it = "буратино" : string

  • Функция переопределяет операцию сложения — теперь сложение это есть умножение:

    - fun sum1 + sum2 = sum1 * sum2;
    > val + = fn : int * int -> int
    - 2 + 3;
    > val it = 6 : int

    Данный синтаксис (когда знак функции записывается между двумя ее аргументами) называют инфиксной записью.

  • Конструкцию fun применима для определения функций, возвращающих другие функции. В таком случае, вместо одного аргумента указывается последовательность аргументов. Функция sq вычисляет квадрат суммы двух чисел (x * x + 2 * x * y + y * y):


    - fun sq x y = x * x + 2 * x * y + y * y;
    > val sq = fn : int -> int -> int
    - sq 2 3;
    > val it = 25 : int
    - val s = sq 2;
    - s 3;
    > val it = 25 : int

    [note]Функция называется каррированной функцией от n аргументов, если вместо одного аргумента при определении функции указывается последовательность из n аргументов.[/note]

    В приведенном примере, sq — каррированная функция от двух аргументов (x и y).

  • Если образцов-аргументов несколько…
    • Если образцов-аргументов, каждому из них сопоставляется свое тело функции.
    • Сопоставления отделяются друг от друга вертикальной чертой | (или).

    Определения функции isNil с двумя образцами-аргументами:


    - fun isNil (nil) = true
    | isNil (_ :: _) = false;
    > val 'a isNil = fn : 'a list -> bool
    - isNil [24, 15];
    > val it = false : bool
    - val sp = [];
    > val 'a sp = [] : 'a list
    - isNil sp;
    > val it = true : bool

    Определение функции с помощью разбора случаев

    Если тип аргумента определяемой функции (fact) имеет более одного конструктора, то определение функции должно содержать по одному предложению на каждый конструктор. Это означает то, что функция может принять любой аргумент данного типа.

    Функция вычисления факториала (прямая рекурсия):


    - fun factDirect x =
    if x < 2 then 1 else x * factDirect (x - 1); > val factDirect = fn : int -> int
    - factDirect 4;
    > val it = 24 : int

    Функция вычисления факториала с помощью разбора случаев (прямая рекурсия):

    - fun factDirectTwo 0 = 1
    | factDirectTwo 1 = 1
    | factDirectTwo x = x * factDirectTwo (x - 1);
    > val factDirectTwo = fn : int -> int
    - factDirectTwo 4;
    > val it = 24 : int

    Функция вычисления факториала с помощью хвостовой рекурсии:


    - fun factTail x =
    let
    fun factIter (i, p) =
    if i > x then p
    else factIter ((i + 1), (p * i))
    in
    factIter (2, 1)
    end;
    > val factTail = fn : int -> int
    - factTail 4;
    > val it = 24 : int

    Лямбда-выражение или fn
    • Функциональное значение можно получить без привязки значения к идентификатору. Для этого используется выражение fn.
    • Общий вид выражения fn: fn a => e, где a — образец аргумента; e — выражение, сопоставляемое с образцом.
    • Безымянная функция fn не может быть рекурсивной, т.к. на нее невозможно сослаться в процессе определения.

    • - fun sp x = [x];
      > val 'a sp = fn : 'a -> 'a list
      - val spTwo = fn x => [x];
      > val 'a spTwo = fn : 'a -> 'a list
      - sp 19;
      > val it = [19] : int list
      - spTwo 12;
      > val it = [12] : int list
      - val y = 4;
      > val y = 4 : int
      - map;
      > val ('a, 'b) it = fn : ('a -> 'b) -> 'a list -> 'b list
      - map (fn x => [x, y]) [40, 50, 70];
      > val it = [[40, 4], [50, 4], [70, 4]] : int list list

      map

      Функция map получает в качестве аргументов функцию и список, а в качестве результата возвращает список, полученный из исходного, путем применения функции-аргумента к каждому его элементу. Причем, тип области определения функции-аргумента должен совпадать с типом элементов списка, а тип ее области значений -произволен.

    • В выражении fn возможно присутствие последовательности пар a => e, разделенных вертикальной чертой | (или).

    В следующем уроке мы рассмотрим полиморфизм и перегрузки в Standart ML.

    Научиться программировать

    • на Delphi

    • на Java

    • на C++