Е.И. Большакова, Н.В. Груздева - Основы программирования на языке Лисп, страница 16
Описание файла
PDF-файл из архива "Е.И. Большакова, Н.В. Груздева - Основы программирования на языке Лисп", который расположен в категории "". Всё это находится в предмете "искусственный интеллект" из 7 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 16 страницы из PDF
Именно внутренние именаатомов служат для их идентификации при работе лисп-интерпретатора.Допущение во внутренних именах атомов любых символов даётвозможность интерпретатору обрабатывать произвольные тексты.Соответственно, в языке Лисп есть возможность считыватьфункциями ввода, а также записывать в лисп-программе атомы, в составкоторых входят особые символы. Кроме пробела и круглых скобок кособым символам относятся апостроф ', обратный слэш \, знаквертикальной черты |, кавычки ", точка с запятой ; , любая цифра, если84она является первым символом имени, а также строчные (малые) буквы,если их регистр существенен.Возможны два варианта записи атома, содержащего особыесимволы: либо перед каждым особым символом поставить знак \ , либозаключить все его символы в знаки вертикальной черты |.
Например, длязадания атома с внутренним именем 2А(80с следует записать\2А\(80\с (как особые указаны: первая цифра, открывающая круглаяскобка и строчная буква с) или |2А(80с|. При записи \2А\(80с(буква с записывается без \) внутреннее имя будет другим: 2А(80С.Кроме внутреннего имени каждый используемый в лисп-программеатом имеет внешнее имя (это одно из указывавшихся ранее четырёх разныхзначений, связанных с атомом). Если атом содержит особые символы, тодля получения внешнего имени внутреннее имя записывается в знакахвертикальной черты, в ином случае внешнее имя совпадает с внутренним.Внешнее имя используется при вводе атома, к примеру:при вводе атома \2А\(80с :(read) => |2А(80С|при вводе атома |2А(80с|:(read) => |2А(80с|.при вводе атома a\(s\)\d:(read) => |A(S)d|.Всё сказанное равно относится к диалектам MuLisp и Common Lisp,за исключением атомов из одного особого символа, внешнее имя которыхв MuLisp записывается со знаком \, например:при вводе атома \1 в MuLisp:(read) => \1при вводе атома \1 в Common Lisp:(read) => |1|Подчеркнём, что обе эти записи \1 и |1| обозначают одно и то жевнутреннее имя символьного атома, состоящего из цифры 1, и не имеютничего общего с числом 1:(symbolp '|1|) => T(symbolp 1) => NIL(eq '|1| '\1) => T(numberp '\1) => NILВажно, что атомы с особыми символами, несмотря на ихспецифическую запись, тем не менее являются такими же атомами, как ивсе остальные: они заносятся в таблицу атомов, могут быть именамифункций или переменных, с ними может быть связан список свойств.Особенность описанной лисповской функции read – считываниеатомов и выражений, сбалансированных по скобкам.
Поскольку в рядеслучаев требуется посимвольный ввод обрабатываемых данных, в Лиспедля этого встроена функция ввода read-char. Обращение к ней имеетвид (read-char), а её значение зависит от диалекта языка Лисп.85В MuLisp значением функции read-char является внешнее имясимвола, считанного из входного потока, например:при вводе символа 1:(read-char) => \1при вводе символа (:(read-char) => \(при вводе символа А:(read-char) => Апри вводе символа a:(read-char) => \аВ Common Lisp введённый символ преобразуется в константусимвольного типа (этот диалект поддерживает несколько типов данных,включая символы, строки, списки).
Символ-константа изображается какпоследовательность трёх знаков: #, \ и сам введённый символ, например:при вводе символа (:(read-char) => #\(при вводе символа А:(read-char) => #\Aпри вводе символа a:(read-char) => #\aCимвольные константы не являются атомами, тем не менее они могутсравниваться на равенство встроенными функциями eq и eql (длякорректной работы eql оба её аргумента должны быть одного типа).Рассмотрим теперь способы ввода и дальнейшей обработки текста,содержащего особые символы (в частности, математических формул, вкоторых есть круглые скобки).
Для этого можно использовать либофункцию посимвольного ввода read-char, либо функцию read,позволяющую ввести весь текст за одно обращение к ней.В Common Lisp для такого ввода функцией read необходимозаключить вводимый текст в кавычки, сделав из него строку, например:"(a+d)/c-12". Введённую строку символов можно преобразовать всписок символов с помощью встроенной функции coerce. Эта функцияимеет два вычисляемых аргумента, значением первого должно бытьпреобразуемое выражение, а значением второго – тип, к которому онодолжно быть преобразовано (в частности: list, string).
В нашем случаефункцию coerce имеет смысл использовать для преобразования строки всписок символов-констант, и для обратного преобразования такого спискав строку:(coerce "(a+d)/c-12" 'list)=> (#\( #\a #\+ #\d #\) #\/ #\c #\- #\1 #\2)(coerce '(#\a #\* #\( #\b #\+ #\c #\) ) 'string))=> "a*(b+c)"После преобразования функцией coerce строки в список символовэтот список уже может быть проанализирован с помощью известныхвстроенных функций, включая функцию eq.86В диалекте MuLisp для единоразового ввода текста с помощьюфункции read необходимо заключить его либо в кавычки, либо в символывертикальной черты.
При этом введённый текст будет сохранен вовнутреннем представлении как символьный атом. Для дальнейшейобработки этого атома может быть использована встроенная в MuLispфункция unpack (распаковка) с одним вычисляемым аргументом.Функция unpack преобразует значение своего аргумента –символьный или числовой атом – в список односимвольных атомов,например:(unpack 'ASD123) => (A S D \1 \2 \3)(unpack '|(a*b)+С|) => ( \( \a * \b \) + С)(unpack '458) => (\4 \5 \8)Обратное преобразование выполняет встроенная в MuLisp функцияpack (упаковка) с одним вычисляемым аргументом.Функция pack преобразует заданный список атомов (не обязательносимвольных и односимвольных) в один символьный атом путёмконкатенации входящих в их запись символов, например:(pack '(AS 127 D 34)) => AS127D34(pack '(127 AS D 34)) => |127ASD34|(pack '(as -12.56 df 34) => AS-12.56DF34(pack '(a |sd| 56 \8t)) => |Asd568T|Заметим, что в исходном списке могут быть числовые атомы, приконкатенации функцией pack берутся символы их записи.Часто в процессе работы программы (в частности, при её отладке)требуется производить выдачу каких-либо сообщений и промежуточныхрезультатов вычислений.
Для этого служат встроенные функции выводалисповских выражений print, prin1, princ и terpri.Функция print с обращением (print e) вычисляет свойаргумент – выражение e и выполняет вывод полученного значения вместес переводом строки. В качестве результата PRINT возвращает значениесвоего аргумента, например:(print (cdr '(a (s) d)) => ((S) D)кроме того, как побочный результат функции этот список-результат будетвыведен на печать или в выходной поток.В диалекте MuLisp print сначала выводит значение выражения eс текущей позиции строки, после чего осуществляет перевод строки. ВCommonLisp сначала происходит перевод строки, а затем выводитсязначение аргумента e и символ пробела.87Функция prin1 от одного аргумента (prin1 e) вычисляет свойаргумент e и выводит полученный результат с текущей позиции строки.Значением функции является вычисленное значение e.
Отличие этойфункции от print - вывод выполняется без перевода строки и бездополнительных пробелов.Функция princ отличается от функции prin1 только тем, что длявывода символьных атомов использует не внешние имена атомов, а ихвнутренние имена, например,(princ '(|as| 23 \(sd\))) => (|as| 23 |(SD)|)но выведено будет выражение (as 23 (SD)).Для перевода строки при выводе служит функция terpri безаргументов, её значением является атом NIL.Ещё одна встроенная функция load служит для загрузки ивычисления файла с лисп-программой.
Вызов функции имеет вид:(load "имя_файла_с_расширением") .Указанный в вызове текстовый файл должен состоять изпоследовательности вычислимых лисповских выражений (форм). Привыполнении вызова load из этого файла последовательно считываются ивычисляются лисповские выражения. Если в процессе ввода и вычисленияочередного выражения обнаружена ошибка, то выводится диагностическоесообщение о ней, а ввод и вычисление следующих выражений из файлапрекращается. В случае успешного вычисления всех выражений иззаданного программного файла выводится сообщение об этом, арезультатом работы функции load является атом Т.Функция load обычно применяется для загрузки определенийновых функций, в частности, после загрузки в системе MuLisp файлаcommon.lsp появляется возможность использования ряда встроенныхфункций диалекта CommonLisp.4.3.Общий вид функций cond, defun, letДо сих пор мы записывали лисповские особые функции cond,defun, let в их простой, хотя и часто используемой форме,применяемой при программировании строго функциональных программ, вкоторых исключены побочные эффекты вычислений.
При использованиифункций с побочными эффектами (в частности, при использованииописанных выше встроенных функций ввода/вывода и функций работы сосписком свойств атомов) может оказаться полезной запись управляющихконструкций cond, defun, let в их более общей форме.88Общий вид условного выражения:(cond (p1 e11 e12 … e1m1)(p2 e21 e22 … e2m2)…(pn en1 en2 … en mn))mi ≥ 0 , n ≥ 1Вычисление условного выражения общего вида выполняется последующим правилам:I.
последовательно вычисляются условия p1, p2, … pn ветвейвыражения до тех пор, пока не встретится выражение pi, значениекоторого отлично от NIL;II. последовательно вычисляются выражения-формы ei1 ei2 … eimiсоответствующей ветви, и значение последнего выражения eimiвозвращается в качестве значения функции cond;III. если все условия pi имеют значение NIL, то значением условноговыражения становится NIL.Как и ранее, ветвь условного выражения может иметь вид (pi), когдаmi=0. Тогда если значение pi ≠ NIL, значением условного выраженияcond становится значение pi.В случае, когда pi ≠ NIL и mi ≥ 2, т.е. ветвь cond содержит болееодного выражения ei, эти выражения вычисляются последовательно, ирезультатом cond служит значение последнего из них eimi.
Такимобразом, в дальнейших вычислениях может быть использовано толькозначение последнего выражения, и при строго функциональномпрограммировании случай mi ≥ 2 обычно не возникает, т.к. значенияпредшествующих eimi выражений пропадают.Использование более одного выражения ei на ветви cond имеетсмысл тогда, когда вычисление предшествующих eimi выражений даётпобочные эффекты, как при вызове функций ввода и вывода, изменениисписка свойств атома, а также определении новой функции с помощьюdefun. К примеру:(cond ((< X 5)(print "Значение х меньше пяти") X)((= X 10)(print "Значение х равно 10") X)(T(print "Значение х больше пяти, но не 10")X))Значением этого условного выражения всегда будет значение переменнойX, но при этом на печать будет выведена одна из трёх строк, в зависимостиот текущего значения X.Во многих диалектах, в том числе в MuLisp и Common Lisp,допускается запись нескольких выражений в теле функции при еёопределении.
Общий вид обращения к defun:89(defun f (x1 … xk) e1 … en) , n ≥ 0Если тело функции пусто, то значение функции равно NIL. В ином случаепоследовательно вычисляются входящие в тело функции выражения ei, ив качестве значения функции берётся значение последнего выражения en.Опять же, включение в тело функции более одного выражения имеетсмысл тогда, когда их вычисление имеет побочный эффект, например:(defun Plus5()(princ "Введите целое число: ")(put 'N 'Value (+ 5 (read)))(terpri)(princ "Число, большее на 5: ")(print (get 'N 'Value)) )При вызове этой функции без аргументов (Plus5) у пользователязапрашивается число, и после его ввода оно увеличивается на 5 исохраняется в списке свойств атома N.