Ремонт принтеров, сканнеров, факсов и остальной офисной техники


назад Оглавление вперед




[9]

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

Рассмотримфункции, которые вырабатывают функции в ка-

честве результата. Пусть f - такая функция. Что тогда можно сказать о ее типе? Пусть она имеет один аргумент типа т и вырабатывает результат типа а->р. Тогда тип функции f есть т->(а->р). Результат применения функции f к аргументу типа т есть функция типа о~->р, которая может быть применена к аргументу типа о и выработать результат типа р. Такое последовательное применение записывается как f(e\) (в2), или просто ie\&2- Заметьте, что это не то же самое, что f(e\,e2)\ (ei,e2) есть один объект - упорядоченная пара, и f(e\, e2) означает "применить функцию f к упорядоченной паре (e\,в2)", в то время как ie\t2 означает "применить f к ei, получить функцию и приме нить ее к в2". Теперь становится понятным, почему ранее при объяснении понятия применения функции к аргументу мы подчеркивали, что функция вычисляется: здесь мы получили пример того, что функция задана не идентификатором, а сложным выражением.

Приведем несколько примеров, проясняющих сказанное:

-fun times (x:int) (у:int) = х*у;

>val times = fn : int->(int->int)

-val twice = times 2;

>val twice = fn: int->int

-twice 4;

>8 : int

-times 3 4;

>12: int

Функция times определена как функция, берущая в качестве аргумента целое число и вырабатывающая функцию, берущую в качестве аргумента целое число и вырабатывающую целое число9. Идентификатор twice

Необходимость ": int" при х и у будет объяснена далее в разделе 2.6.


привязывается к значению times 2. Поскольку 2 имеет тип int, функция times может быть применена к 2, и результатом будет объект типа int->int - как это и видно из сообщения о типе twice. Так как twice есть функция, она может быть применена к аргументу - ив нашем примере результат вычисления twice 4 есть 8 (разумеется!). Наконец, times применяется к 3, и затем результат этого применения применяется к 4, в результате чего получается 12. В этом последнем выражении подразумевается следующая расстановка скобок: (times 3) 4.

Столь же свободно могут использоваться функции, получающие другие функции в качестве аргументов. Такие функции часто называют функционалами или операторами (но, опять подчеркиваем, в ML такая терминология может указывать только на содержательное использование функций, а не на какие-то особые их языковые свойства). Классическим примером функции такого рода является функция тар. Она получает в качестве аргументов функцию и список, и возвращает в качестве результата список, полученный из исходного применением функции-аргумента к каждому его элементу. Тип области определения функции-аргумента должен совпадать с типом элементов списка, но тип ее области значений произволен. Вот ее определение на ML:

-fun map f nil = nil

I map f (hd::tl) = f(hd) :: map f tl;

>val map = fn : (a->b) -> (a list) -> (b list)

Обратите внимание на то, что тип тар отражает связь между типом области определения функции-аргумента и типом элементов списка-аргумента, а также между типом области значений функции-аргумента и типом элементов списка-результата.

Вот несколько примеров использования функции тар:

-val 1st = [1,2,3,4,5];

>val 1st = [1,2,3,4,5] : int list

-map twice 1st;

>[2,4,6,8,10] : int list

-fun listify x = [x];

>val listify = fn : a -> a list

-map listify 1st;

>[[1], [2], [3], [4], [5]] : int list list

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

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


-fun compose (f, g) (x) = f(g(x));

>val compose = fn : (a->b * c->a) -> Cc->b)

-val fourtimes = compose (twice, twice);

>val fourtimes = fn : int -> int

-fourtimes 5;

>20 : int

Давайте рассмотрим этот пример внимательно. Функция compose получает в качестве аргумента пару функций (f ,g) и возвращает в качестве результата функцию; эта функция, будучи применена к аргументу х, возвращает в качестве результата f (g(x)). Поскольку результат есть f (g(x)), тип х должен быть типом области определения g; поскольку f применяется к g(x), тип области определения f должен совпадать с типом области значений g. Таким образом мы получаем тип compose, который был сообщен ML-системой. Функция fourtimes получается путем применения compose к паре функций (twice .twice). Результатом будет функция, которая, будучи применена к х, возвратит twice (twice (х)); в нашем случае, когда х есть 5, результатом является 20.

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

-fun listify х = [х];

>val listify = fn : a -> a list

-val listify2 = fn x => [x];

>listify2 = fn : a -> a list

-listify 7;

>[7] : int list

-listify2 7;

>[7] : int list

-(fn x => [x]) (7);

>[7] : int list

-val 1st = [1, 2, 3] ;

>val 1st = [1, 2, 3] : int list

-map (fn x => [x], 1st );

>[[1], [2], [3]]: int list list



[стр.Начало] [стр.1] [стр.2] [стр.3] [стр.4] [стр.5] [стр.6] [стр.7] [стр.8] [стр.9] [стр.10] [стр.11] [стр.12] [стр.13] [стр.14] [стр.15] [стр.16] [стр.17] [стр.18] [стр.19] [стр.20] [стр.21] [стр.22] [стр.23] [стр.24] [стр.25] [стр.26] [стр.27] [стр.28] [стр.29] [стр.30] [стр.31] [стр.32]