Понятие функции

Функция - отображение между множествами

  Функцией в математике называется отображение, которое однозначно отображает одни значение на другие. Например, запись

  y=f(x)

ставит в соответствие каждому элементу x из множества определения единственный элемент y из множества значений функции f. Это соответствие также можно записать в следующем виде:

  f(x) → y

  Будем говорить, что функция f от аргумента x имеет значение y=f(x).
  У функции может быть произвольное количество аргументов, в том числе их может не быть совсем. Приведём примеры функций:

abs(-3) → 3 абсолютная величина числа
+(2,3) → 5 сумма
union((a,b),(c,b)) → (a,b,c) объединение множеств
финский(John) → ложь определение языка
дети(адам,ева) → (каин,авель) множество детей

Тип аргументов и функций

  В общем случае функция может задавать отображение из нескольких множеств в множество значений. Для изображения типов значений функции и её аргументов позаимствуем ещё одно принятое в математике обозначение. Пусть f - функция от двух аргументов:

  f(x,y) → z  отображение пар элементов x,y в множество z

  Типы аргументов x и y, а также тип значения z можно обозначить следующим образом:

  f: A × B → C

  Здесь знак × между A и B обозначает прямое произведение множеств. Приведённая запись означает, что тип первого аргумента функции f есть A (т.е. он принадлежит множеству A), тип второго аргумента - B и тип значения функции - C. Функция f ставит в соответствие упорядоченным парам, образованным из элементов множеств A и B, элемент из множества C.
  Приведём типы значений и аргументов функций из предыдущего примера:

ФУНКЦИЯАРГУМЕНТЫЗНАЧЕНИЯ
absчислочисло
+число × числочисло
unionмножество × множествомножество
финскийсловологическое значение
детичеловек × человекмножество людей

Определение и вызов функции

  Определение функции в языке Лисп позволяет задать произвольное вычислимое отображение. Например, функцию, вычисляющую сумму квадратов, можно определить с помощью сложения и умножения:

суммаквадратов:
  аргументы: x,y
  значение: x*x+y*y

  Здесь x и y переменные, представляющие произвольные числа.
  Вызов функции означает запуск, или применение определения функции к конкретным значениям аргументов. Например, применим функцию суммаквадратов к аргументам x=2 и y=3:

  суммаквадратов(2,3) → 13

Единообразная инфиксная нотация

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

  f(x), g(x,y), h(x,g(y,z)) и т.д.

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

  x+y, x-y, x*(y+z) и т.д.

  В данной реализации языка Лисп принята инфиксная форма записи, при которой объект, имя функции и аргументы записываются внутри скобок.

  (x F), (x G y), (x H (y G z)) и т.д.

  Таким же образом записываются арифметические действия. Приведённые выше арифметические выражения в лисповской инфиксной записи выглядят так:

  (x + y), (x - y), (x * (y + z)) и т.д.

  Сначала этот способ записи покажется затруднительным, но он весьма быстро станет привычным и даже понравится по мере того, как станут очевидны его преимущества.
  Лисп содержит также и средства, необходимые для задания способа записи и расширения языка в желаемом направлении. Можно использовать традиционный способ записи либо придумать себе новый способ. Так как всё составлено из списков, то преобразование из одной нотации в другую довольно просто. Некоторые Лисп-системы позволяют без дополнительного программирования использовать арифметические операции без излишних скобок.
  Единообразная и простая структура вызова функции удобна как для программиста, так и для вычисляющего значения выражений интерпретатора Лиспа. Вычисляя значения функций, не нужно осуществлять сложный синтаксический анализ выражений, так как сразу по первому символу текущего выражения система уже знает, с какой структурой имеет дело и как с ней обращаться.

Аналогия между Лиспом и естественным языком

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

  (шведский переведи (Знаю шведский))

  Запись вызова функции не использует такого числа технических деталей и соглашений, как это обычно принято в языках программирования. Если не принимать во внимание используемые для выделения структур скобки и искусственное объединение нескольких слов, то с точки зрения записи нет особой разницы между Лиспом и естественным языком.

Диалог с интерпретатором Лиспа

  Лисп напоминает естественный язык и тем, что он обычно используется в диалоге (интерактивно) и в режиме интерпретации. Интерпретатор Лиспа функционирует следующим образом. Когда пользователь заканчивает ввод какого-либо вызова функции или другого выражения, то интерпретатор вычисляет и выдаёт значение этого выражения. Если мы хотим, например, вычислить выражение (2 + 3), то введём его в машину и сразу получим результат:

>(2 + 3)  ;пользователь вводит выражение
5  ;интерпретатор вычисляет и выдаёт результат

  Символ >, показанный перед вводимым выражением, - это так называемое приглашение, при помощи которого интерпретатор даёт знать, что он выполнил вычисление предыдущего выражения и ждёт новое выражение.

>(3 - 2)  ;вычитание
1
>  ;интерпретатор выдаёт на экран приглашение и ждёт ввода очередного выражения

Иерархия вызовов

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

  ((1 + 2) * (3 + 4))

  Вычисляется сначала первая задача (1 + 2) и результат определяет объект (в данном случае число), затем второй элемент списка интерпретируется как функция определенная именно для чисел, затем эта функция вычисляет параллельно все оставшиеся аргументы и применяется непосредственно. Если эти аргументы будут вложенными вызовами функции или другая вычислимая форма, то перед её вычислением определяются значения её аргументов и т.д.
  Наконец, опустившись на самый нижний уровень, можно начать в качестве значений аргументов предыдущего уровня подставлять значения, даваемые вложенными вычислениями. Последним возвращается значение всего выражения. Как и в математике, в Лиспе в первую очередь вычисляются выражения, заключённые в скобки:

>((1 + 2) * (3 + 4))  ;(1+2)*(3+4)
21
>((1 + 2) * (1 * (2 + 3)))  ;(1+2)*(1*(2+3))
15

' блокирует вычисление выражения

  В некоторых случаях не надо вычислять значение выражения, а нужно само выражение. Нас, например, может не интересовать значение функционального вызова (2 + 3), равное 5, а мы хотим обрабатывать форму (2 + 3) как список. В таких случаях выражения, которые интерпретатору не надо вычислять, помечаются особым образом. Чтобы предотвратить вычисление значения выражения, нужно перед этим выражением поставить апостроф '. В Лиспе начинающееся с апострофа выражение соответствует заключению в кавычки в обыкновенном тексте, и в обоих случаях это означает, что цитату нужно использовать в том виде, как она есть. Например:

>'(2 + 3)
(2 + 3)
>'y
y

  Числа не надо предварять апострофом, так как интерпретатор считает, что число и его значение совпадают. Однако в принципе использование апострофа перед числом не запрещено. Перед другими константами (nil, true и false), как и перед числами, тоже не надо ставить апостроф, поскольку и они представляют самих себя.
  Однако заметим, что в приведённых примерах на месте имени функции в функциональном вызове апостроф не использовался.

>317.0  ; апостроф не нужен
317
>('2 + 3)
5
>true
  ; значением true является true
true
>'true
  ; апостроф не нужен
true
>nil
  ; nil представляет самого себя
nil

  Представленный интерпретатор не допускает использование в качестве функции вычисляемого выражения.