
- •3.В языке lisp лямбда-выражение имеет вид: (lambda (x1 x2 ... Xn) fn)
- •20. Применяющие функционалы дают возможность интерпретировать и преобразовывать данные в программу и применять её в вычислениях.
- •Пример,
- •27. В Прологе отношение между объектами называется фактом (fact).
- •27 April 1992
- •Определение.
- •37. Процедурная семантика (процедурный смысл) пролог-программы определяет , как пролог-программа отвечает на вопросы. Ответитиь на вопрос -зто значит удовлетворить цели.
3.В языке lisp лямбда-выражение имеет вид: (lambda (x1 x2 ... Xn) fn)
Функция LAMBDA предназначена для определения "безымянных" функций и называется вычисляемой функцией. Первый аргумент вычисляемой функции - (X1 X2 ... XN) является списком (возможно, пустым!). Его называют лямбда-списком. X1, X2,...,XN называются формальными параметрами. Формальность параметров означает, что их можно заменить на любые другие символы, и это не отразится на вычислениях, определяемых функцией. Именно это и скрывается за лямбда-нотацией. Второй аргумент функции LAMBDA - FN называется телом. Оно представляет собой произвольное выражение, значение которого может вычислить интерпретатор языка LISP. Таким образом:
Для
того, чтобы применить лямбда-функцию к
аргументам, нужно в вызове функции
поставить лямбда-выражение вместо имени
функции:
( (LAMBDA (X1 X2 ... XN) FN) A1 A2 ... AN)
Здесь
Ai (i=1,2,...,N) - выражения языка LISP, определяющие
фактические параметры. Такую форму
вызова называют лямбда-вызовом.
Вычисление
лямбда-вызова (применение лямбда-выражения
к фактическим параметрам) производится
в два этапа. Сначала вычисляются значения
фактических параметров и соответствующие
формальные параметры связываются с
полученными значениями. Этот этап
называется связыванием параметров.
На
следующем этапе с учетом новых связей
вычисляется тело лямбда-выражения, и
полученное значение возвращается в
качестве значения лямбда-вызова.
Формальным параметрам после окончания
вычисления возвращаются те связи,
которые у них были перед вычислением
лямбда-вызова.
Весь этот процесс
называют лямбда-преобразованием
(лямбда-конверсией).
Приведем несколько
примеров лямбда-преобразований:
$
((LAMBDA (NUM) (PLUS NUM 1)) 45) 46 $ ((LAMBDA (M N) (COND ((LESSP M
N) M) (T N))) 233 12
23
$ (LAMBDA NIL (PLUS 3 5)) 8 $ (LAMBDA () (PLUS 3 5)) 8 $ ((LAMBDA (X)
(EQ X NIL)) NIL) T
Лямбда-выражение - это
"безымянная" функция, которая
пропадает тотчас же после
лямбда-преобразования. Ее трудно
использовать снова, так как нельзя
вызвать по имени, хотя лямбда-вызов
доступен как списочный объект.
"Безымянные"
функции используются, например, при
передаче функции в качестве аргумента
другой функции или при формировании
функции в результате вычислений, другими
словами, при синтезе программ.
5. В Lisp'е для вызова функции принята единообразная префиксная форма записи, при которой как имя функции, так и ее аргументы записываются в виде элементов списка, причем имя функции - это всегда первый элемент списка.
6. В некоторых случаях не требуется вычисления значений выражений, а требуются само
выражение. Если прямо ввести * ( + 2 3 ) , то 5 получится как значение. Но можно понимать ( + 2 3 ) не как функцию, а как список. S-выражения, которые не надо вычислять,
помечают для интерпретатора апострофом " ' " (quote).
QUOTE - специальная функция с одним аргументом, которая возвращает в качестве значения этот аргумент.
7.
Чтобы определить функцию, необходимо: Дать имя функции Определить параметры функции Определить ,что должна делать функция |
|
Для задания новых функций в Лиспе используется специальная форма defun.
( defun < имя-функции > < параметры > < тело-функции >)
Вызов функции: ( < имя-функции > < значения аргументов >)
8. В Lisp'е передача параметров в функцию осуществляется по значению. Параметры используются для того, чтобы передать данные в функцию, а результат функции возвращается как ее значение, а не как значение параметра, передаваемого по ссылке.
Например:
;возвращаемое значение 8
(+ 3 5)
;возвращаемое значение 12
(* (+ 1 2) (+ 3 4))
;возвращаемое значение 0
(sin 3.14)
Формальные параметры функции в Коммон Лиспе называют лексическими или статическими переменными. Связи статической переменной действительны только в пределах той формы, в которой они определены. Статические переменные представляют собой лишь формальные имена других лисповских объектов. После вычисления функции, созданные на это время связи формальных параметров ликвидируются и происходит возврат к тому состоянию, которое было до вызова функции.
Возникшие в результате побочного эффекта изменения значений свободных переменных, т.е. используемых в функции, но не входящих в число ее формальных параметров, остаются в силе после окончания выполнения функции.
11. Для выполнения операции присваивания используется функция set. Формат функции (set variable value). Причем, если не требуется вычисления аргументов, их нужно предварить апострофами. Например:
> (set 'x 5)
5
> x
5
> (set 'y (+6 12))
18
> y
12
> (set 'a 'b)
B
> (set a 'c)
C
> a
B
> b
C
> c
ERROR: UNBOUND VARIABLE - C
Вместо функции set можно использовать функцию setq, также выполняющую присваивание, но при ее использовании нет необходимости предварять первый аргумент апострофом. Для первого аргумента блокировка вычисления выполняется автоматически.
> (setq x 5)
5
> x
5
> (setq y (+6 12))
18
> y
18
12. Управляющие структуры делятся на группы. Одна из групп - разветвления вычислений.
В нее входят:
- условные предложения: COND IF WHEN UNLESS
( COND ( < проверка-1 > < действие-1 > )
( < проверка-2 > < действие-2 > )
...............................................................
( < проверка-n > < действие-n > ))
В качестве аргументов < проверка > и < действие > могут быть произвольные формы.
Значение COND определяется следующим образом:
Выражения < проверка-i >, выполняющие роль предикатов вычисляются последовательно, слева направо, до тех пор, пока не встретится выражение, значением которого не является NIL.
Вычисляется результирующее выражение, соответствующее этому предикату, и полученное значение возвращается в качестве значения всего предложения COND.
Если истинного значения нет, то значением COND
будет NIL.
Обычно в качестве последнего условия пишется t, соответствующее ему выражение будет вычислятся в тех случаях, когда ни одно другое условие не выполняется.
Последнюю строку можно записать: ( t ' atom ) ) )
Пример: ( функция проверяет тип аргумента)
( defun classify ( arg )
( cond
( ( null arg ) nil )
( ( list arg ) 'list )
( ( numberp arg ) 'number )
( t 'atom ) )
* ( classify 'a )
atom
* ( classify 5 )
Number
В COND могут отсутствовать результирующие выражения для предикатов, а так же присутствовать несколько действий.
( COND
( < проверка-1 > )
( < проверка-2 > < действие-2 > )
(< проверка-3 > < дейст.-31 > < дейст.-32 > < дейст.-33 >))
Если нет действия - результат значение предиката.
Если не одно действие - результат значение последнего аргумента.
( IF < условие > < то форма > < иначе форма > )
Пример:
( if ( atom x ) 'аtоm 'not - аtom )
Условные предложения WHEN и UNLESS являются частными случаями условного предложения IF:
Если условие соблюдается, то выполняются формы.
( WHEN < условие >
< форма-1 > < форма-2 > < форма-3 > ... )
Если условие не соблюдается, то выполняются формы.
( UNLESS < условие >
< форма-1 > < форма-2 > < форма-3 > ... )
14. READ отличается от операторов ввода-вывода других языков программирования, тем что он обрабатывает вводимое выражение целиком, а не одиночные элементы данных.
( READ ) функция без аргументов.
Как только интерпретатор встречает READ, вычисления приостанавливаются до тех пор пока пользователь не введет какой-либо символ или выражение.
* ( READ )
new - выражение пользователя
new - значение функции.
READ не указывает на ожидание информации. Если прочитанное выражение необходимо для дальнейшего использования, то READ должен быть аргументом какой либо формы, которая свяжет полученное выражение:
* ( setq x ' ( read ) )
( + 1 2 ) - вводимое выражение
( + 1 2 ) - значение
* x
( + 1 2 )
* ( eval x )
3
Функция PRINT - это функция с одним аргументом. Она выводит значение аргумента на монитор, а затем возвращает значение аргумента.
Для вывода выражений можно использовать функцию print.
( PRINT < arg >)
print перед выводом аргумента переходит на новую строку,
а после него выводит пробел.
* ( print ( + 2 3 ) )
5 - вывод
5 - значение
print и read - псевдофункции, у которых кроме значения есть побочный эффект.
Значение функции - значение ее аргумента.
Побочный эффект - печать этого значения.
Это не значит, что всегда две строки. Только когда print на высшем уровне, чего практически не бывает.
Пример:
* ( setq row ' ( x x x ) )
( x x x )
* ( print ( cdr row ) )
( x x ) - печать
( x x ) - значение
* ( cons 'o ( print ( cdr row ) ) )
( x x ) - печать
( o x x ) – значение
15. Все переменные в LISP делятся на два вида: глобальные и локальные. Глобальные переменные постоянно находятся в оперативной памяти и доступны из любой функции загруженной в тот же сеанс. Глобальные переменные создаются автоматически при присваивании им значения вне тела функции или внутри функции, если переменная с таким именем не объявлена локальной. Локальные переменные явно описываются в заголовке функции defun и видны только внутри этой функции. В начале работы функции локальные переменные имеют значение nil (если не было выполнено явное присваивание значений), а по окончании работы этой функции они автоматически удаляются из памяти. Аргументы функций также являются локальными переменными. В начале работы функции аргументы принимают значения, переданные функции при ее вызове.
Предложение let служит для одновременного присваивания значений нескольким переменным. Формат предложения let:
(let ((var_1 value_1) (var_2 value_2) … (var_n value_n)) form_1 form_2 … form_m)
Работает предложение следующим образом: переменным var_1, var_2, … var_n присваиваются (параллельно!) значения value_1, value_2, … value_n, а затем вычисляются (последовательно!) значения форм form_1 form_2 … form_m. В качестве значения всего предложения let возвращается значение последней вычисленной формы form_m.
> (let ((x 3) (y 4)) (sqrt (+ (* x x) (* y y))))
5.0
Так как присваивание значений переменным выполняется параллельно, то в следующем примере будет выдано сообщение об ошибке.
> (let ((x 3) (y (+ 1 x))) (sqrt (+ (* x x) (* y y))))
ERROR: UNBOUND VARIABLE - X
После завершения выполнения предложения let переменные var_1, var_2, … var_n получают значения, которые они имели до использования в этом предложении, то есть предложение let выполняет локальные присваивания.
> (setq x 'three)
THREE
> (setq y 'four)
FOUR
> (let ((x 3) (y 4)) (sqrt (+ (* x x) (* y y))))
5.0
> x
THREE
> y
FOUR
16. Функция является рекурсивной, если в ее определении содержится вызов самой этой функции.
Определим функцию MEMBER
(defun MEMBER (item list)
(cond ((null list) nil)
((eql (car list) item) list)
(t (MEMBER item (cdr list)))))
Предположим, что необходимо написать функцию sumall, которая имеет один аргумент, целое положительное число и возвращает сумму всех целых чисел между нулем и этим числом.
Например (sumall 9) должно вернуть 45
Это можно решить циклически; мы решим рекурсивно.
Отметим два факта:
1. Если n=0, сумма чисел между 0 и n равна 0.
2. Если n>0, сумма чисел между 0 и n равна n плюс сумма чисел между 0 и n-1.
Эти два факта переводятся непосредственно в определение функции:
(defun sumall (n)
(cond ((zerop n) 0)
(t (+ n (sumall (- n 1))))))
Производится проверка n : равно нулю или нет.
Если значение n=0, то функция возвращает 0 .
В противном случае , функция вызывает сама себя для вычисления суммы чисел между 0 и n-1 и и добавляет n к этой сумме.
Основная рекурсия над списками CDR рекурсия.
Написать функцию list-sum которая берет один аргумент, список чисел, и возвращает сумму этих чисел.
Последовательно упрощающимся аргументом в этом случае будет список. Упрощение списка (cdr lis). Последнее значение аргумента nil.
Составим рекурсивную таблицу для (list-sum lis)
Шаг 1. Завершение (Терминальная ветвь)
(list-sum nil) = 0 - значение
Шаг 2. Рекурсивная ветвь
Рекурсивные отношения между
(list-sum lis) и (list-sum (cdr lis))
2а. Примеры рекурсии
(list-sum '(2 5 3)) =10 (list-sum '(5 3)) = 8
(list-sum '(3)) =3 (list-sum nil )=0
2b. Характеристическое рекурсивное отношение (list-sum lis) может быть получена из
(list-sum (cdr lis)) сложением с (car lis).
Текст функции.
(defun list-sum (lis )
(cond ((null lis) 0)
(t (+ (car lis) (list-sum (cdr lis))))))
Две терминальные ветви будут в том случае, когда ведется поиск цели в последовательности значений и мы желаем получить результат, как только цель найдена.
Ветвь 1. Цель найдена и надо вернуть ответ
Ветвь 2. Цель не найдена и нет больше элементов.
Написать функцию greaternum.
Она имеет два аргумента : список чисел и заданное число. Функция возвращает первое число в списке превышающее заданное. Если этого числа нет - возвращается заданное число.
Программа.
(defun greaternum (lis num)
(cond ((null lis) num)
((> (car lis) num) (car lis))
(t (greaternum (cdr lis) num))))
Несколько рекурсивных ветвей может понадобиться, если функция обрабатывает все элементы в структуре, но использует некоторые элементы отлично от других.
В этом случае составляются два рекурсионных отношения.
Пример. Напишите функцию negnums, которая получает список чисел и возвращает список, который содержит только отрицательные числа (0 положителен).
(negnums '(-1 5 -6 0 2)) возвращает (-1 -2)
Шаг 1. Завершение (Терминальная ветвь)
(negnums nil) = nil
Шаг 2. Рекурсивная ветвь
Рекурсивные отношения между (negnums l) и (negnums (cdr l))
1. (car l ) < 0
2а. Примеры рекурсии
(negnums '(-5 3)) =(-5)
(negnums '(3)) = nil
(negnums '(-5 3 -6 0 )) =(-5 -6)
(negnums '( 3 -6 0 )) =(-6)
2b. Характеристическое рекурсивное отношение (negnums l) может быть получена из (negnums (cdr l)) (cons (car l) (negnums (cdr l))
2. (car l ) >= 0
2а. Примеры рекурсии
(negnums '(1 -5 3 -6 0 )) =(-5 -6)
(trace negnums) '(- 5 3 -6 0 )) = (-5 -6)
2b. Характеристическое рекурсивное отношение (negnums l) может быть получена из (negnums (cdr l))
(negnums l) = (negnums (cdr l)
Программа
(defun negnums (l)
(cond ((null l) nil)
((< (car l) 0) (cons (car l) (negnums (cdr l))))
(t (negnums (cdr l)))))
18. Оперативная память, в которой хранятся S-выражения, обычно делится на две больших области: список объектов и область списочных ячеек. В списке объектов хранятся атомы. Каждый атом занимает блок памяти переменного размера. В этом блоке хранится символьное изображение атома и ряд его дополнительных характеристик. Область списочных ячеек состоит из блоков фиксированного размера. Каждая списочная ячейка хранит два адреса, которые по историческим причинам называются А-указательи D-указатель. Эти адреса могут указывать как на атомы (т.е. хранить адреса областей из списка объектов), так и на другие списочные ячейки. Для наглядного изображения списков существуют уже устоявшаяся традиция. Списочная ячейка предствляется прямоугольником, разделеным вертикальной линией на две равные части. В левой части хранится A-указатель, в правой - D-указатель. Атомы изображаются символами (буквами или цифрами). Теперь можно сказать, что точечная пара (A . B) - это одна списочная ячейка, в A-указателе которой находится адрес атома A, а в D-указателе - адрес атома B. Графически точечную пару изображают так:
или так:
А как можно представить графически список (A)? Поскольку список (A) есть точечная пара (A . Nil), то графическое изображение строится сразу:
Не представляет труда построить графическое изображение и более сложных списков. Так, например, список (A B C D) устроен так:
Полезно соотнести это представление с точечной записью списка (A B C D), которое имеет вид (A . (B . (C . (D . Nil)))). Вот еще один пример внутреннего представленния списка более сложной структуры. Список ((A B) (C D)) хранится в памяти в следующем виде:
19. Список – это перечень произвольного числа элементов, разделенных пробелами, заключенный в круглые скобки. Пустой список - это атом Nil.
Примеры
((A B) C)
((A B) (D C))
((A B)(D(C E)))
Элементарные функции для работы со списками (есть в любом лиспе):
CONS - Функция, которая строит списки из бинарных узлов, заполняя их парами объектов, являющихся значениями пары ее аргументов. Первый аргумент произвольного вида размещается в левой части бинарного узла, а второй, являющийся списком, - в правой.
CAR – Функция, обеспечивающая доступ к первому элементу списка - его "голове".
CDR – Функция, укорачивающая список на один элемент. Обеспечивает доступ к "хвосту" списка, т.е. к остатку списка после удаления его головы.
ATOM - Функция, различающая составные и атомарные объекты. На атомах ее значение "истина", а на более сложных структурах данных – "ложь". ATOM ( A B ) = Nil, потому что список. (ATOM ()) = T.
EQ – Функция, которая проверяет атомарные объекты на равенство.
S-выражение - это или атом или заключенная в скобки пара из двух S-выражений, разделенных точкой. То есть, либо атом, либо любое количество бинарных узлов.
Любое S-выражение может быть построено из атомов с помощью CONS и любая его часть может быть выделена с помощью CAR-CDR
Применяются также композиции типа CAAR - car от car, CADR и так далее. В композиции вычисляются в обратном записи порядке (напр CADR - сначала CDR потом CAR). (cADr '(A B C)) = B
Обзор функций Clisp по работе с данными:
(Append Список … ) Сцепляет списки, полученные как аргументы (Assoc Атом А-список) Находит в А-списке пару, левая часть которой – Атом Atom Проверка на атомарность Car Первый элемент списка или левый элемент структуры Cdr Результат удаления первого элемена из списка или правый элемент структуры Cons Создание узла из двух элементов (Eq Данное1 Данное2) Истина при идентичных данных (Equal Структура1 Структура2 ) Истина при эквивалентных структурах (Delete Объект Список ) Строит копию Списка без заданного объекта (Intersection Список … ) Пересечение списков (Last Список ) Последний элемент структуры, представляющей список. Можно задавать длину завершающего отрезка списка. (Length Список ) Длина списка (List Форма … ) Строит список из значений Форм (Member Объект Список ) Ищет Объект в Списке (Null Форма) Истина для Nil (Pairlis Атомы Данные А-спиок) Пополняет А-список парами из Атомов и значений, соответствующих Данных. (Reverse Список ) Копия Списка с обратным порядком элементов (Set-difference Список … ) Разность множеств, представленных Списками (Sort Список Предикат ) Упорядочивает Список согласно Предикату (Sublis А-список Структура ) Преобразует Структуру согласно А-списку методом подстановки данных вместо связанных с ними атомов. (Subst Новое Старое Структура ) Преобразует Структуру, заменяя Старое на Новое. (Union Список … ) Объединение множеств, представленных Списками.