НОВОСТИ   БИБЛИОТЕКА   ЮМОР   КАРТА САЙТА   ССЫЛКИ   О САЙТЕ  




предыдущая главасодержаниеследующая глава

Оперирование символами

Как и для большинства других языков программирования, лучший способ изучения языка Лисп - это смелый прыжок в область интересных программ. Поэтому мы будем время от времени бросать взгляд на программный код, содержащий свойства, которые пока что будут находиться вне пределов полного понимания читателя, имея в виду как можно быстрее перейти к интересным приложениям.

Чтобы приступить к делу, представьте себе, что вы находитесь перед вычислительной машиной. Представьте себе, что вы ведете беседу, в ходе которой вы набираете нечто на терминале, и Лисп выдает вам "ответы". Предположим, например, что вам требуется помощь при сложении чисел. Правильной записью будет такая:

 (PLUS 3.14 2.71) 

на что Лисп охотно ответит

Это очень простой пример способности Лиспа работать с элементарными арифметическими операциями. Точно так же легко строятся простые примеры манипулирования с символами. Предположим, например, что нам нужно следить за некоторыми фактами, требуемыми для понимания детского рассказа, скажем о роботе. Может оказаться важным запомнить, что некоторые дети являются товарищами робота. Обычно для такой группы необходимо иметь название, и слово FRIENDS (ДРУЗЬЯ)* подойдет точно так же, как и многие аналогичные слова. Если друзья- это Дик, Джейн и Салли, то указанный факт можно отразить, набрав на терминале

 (SETQ FRIENDS '(DICK JANE SALLY)) 

* (Поскольку в большинстве распространенных сегодня систем Лиспа используются лишь латинские буквы, а все "библиотечные" функции имеют заведомо английское название, то ниже мы всюду, где это возможно, сохраним авторские примеры, давая лишь необходимые пояснения.- Прим. перев.)

Стоящую здесь одиночную кавычку мы пока что оставим без внимания. SETQ ассоциирует FRIENDS с (DICK JANE SALLY), так что, набрав слово FRIENDS, мы получим в качестве ответа список друзей:

FRIENDS 
 (DICK JANE SALLY) 

Аналогичный список мог бы существовать и для недругов (ENEMIES)

 (SETQ ENEMIES '(TROLL GRINCH GHOST)) 

Но поскольку в детском мире друзья и враги - изменчивые категории, часто появляется необходимость изменить категорию отдельного конкретного индивида. Призрак (GHOST) перестает быть врагом и становится другом после набора на терминале следующих двух строк:

 (SETQ ENEMIES (DELETE 'GHOST ENEMIES)) 
 (SETQ FRIENDS (CONS 'GHOST FRIENDS)) 

Первая строка изменяет запомненный список врагов на то, что он собой представлял раньше, минус входящий в него элемент GHOST. Вторая строка была бы проще, если бы CONS можно было заменить на какое-либо более самоочевидное слово типа слова ДОБАВИТЬ, но здесь мы связаны теми конвенциями, которые были приняты ис-торически. Во всяком случае, списки друзей FRIENDS и врагов ENEMIES теперь изменились, так что мы получим теперь соответствующим образом измененный ответ:

ENEMIES 
 (TROLL GRINCH) 
FRIENDS 
 (GHOST DICK JANE SALLY) 

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

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

 (DEFINE (NEWFRIEND NAME) 
     (SETQ ENEMIES (DELETE NAME ENEMIES)) 
     (SETQ FRIENDS (CONS NAME FRIENDS)))

Располагая такой программой, описанное повышение в ранге Призрака может быть достигнуто более простым путем, если набрать на терминале лишь только

 (NEWFRIEND 'GHOST) 

Данные и программы в Лиспе состоят из 5-выражений

Мы уже должны сделать некоторые важные замечания. Во-первых, запросы к системе Лисп всегда должны быть заключены между правой и левой круглыми скобками. Такое выражение мы называем списком и говорим о его элементах. В нашем самом первом примере список (PLUS 3.14 2.71) имеет три элемента PLUS, 3.14 и 2.71.

Обратите внимание на специфическое положение функции PLUS, которая располагается странным образом перед двумя операндами, которые предстоит сложить, а не между ними, как в обыч ной арифметической нотации. Этот выбор диктуется требованием однородности. В Лиспе операция, которую предстоит выполнить, всегда задается первой, а за ней идут те операнды, с которыми она должна работать, т. е. аргументы функции. Таким образом, 3,14 и 2,71 являются аргументами для функции PLUS.

Рассмотрим еще несколько примеров так называемой префиксной нотации. Пока что мы будем ограничиваться арифметикой, поскольку она проще, чем манипулирование символами. Чтобы все было ясно, ниже приведены также ответы системы Лисп*.

(DIFFERENCE 3.14 2.71) 
   .43 
 (TIMES 9 3) 
    27 
(QUOTIENT 9 3) 
    3 
(MAX 2 4 3) 
    4 
(MIN 2 4 3) 
   2 

* (DIFFERENCE - разность, TIMES - умножить, QUOTIENT - частное, ADD - прибавить, SUB - вычесть, SQRT - квадратный корень, EXPT - возвести в степень.- Прим. перев.)

 (ADD1 6) 
   7 
 (SUB1 6) 
   5 
 (SQRT 4) 
   2 
 (ЕХРТ 2 3) 
   8 
 (MINUS 8) 
   -8 

Из примеров сорока функций языка Лисп около половины составляют арифметические операции со столь же ясной мнемоникой, как и в приведенных случаях. Отметим, что МАХ выбирает наибольшее из чисел, задаваемых в качестве аргумента этой функции. Как и МАХ, функции PLUS и TIMES могут работать более чем с двумя аргументами. Неясностей никогда при этом не возникает, поскольку конец списка аргументов явным образом указывается посредством правой круглой скобки. SQRT, разумеется, вычисляет квадратный корень. Функции ADD1, SUB1, SQRT и MINUS являются функциями, работающими с одним аргументом.

Рассмотрим теперь выражение

 (PLUS (TIMES 2 2) (QUOTIENT 2 2)) 

Если мы рассмотриваем эту запись, как команду что-то сделать, то легко видеть, что подвыражение (TIMES 2 2) имеет значение 4, (QUOTIENT 2 2) -значение 1, и эти результаты поступают на вход функции PLUS, что дает окончательно 5. Но если все выражение рассматривается как некий список, то мы видим, что PLUS является первым элементом, целиком все выражение (TIMES 2 2) является вторым элементом, а выражение (QUOTIENT 2 2) третьим. Таким образом, списки могут быть элементами других списков.

PLUS и 3.14, которые имеют очевидный смысл, точно так же как F 0 0, В27 123XYZ, называются атомами. Атомы и списки часто называются s-выражениями, где s соответствует английскому слову symbolic, означающему символьный.

CAR и CDR позволяют разбирать список на части

Примеры из арифметики просты, но арифметика сама по себе не позволяет продемонстрировать достоинства языка Лисп как средства оперирования с s-выражениями. Предположим, что у нас имеется s-выражение вида (FAST COMPUTERS ARE NICE) (БЫСТРЫЕ КОМПЬЮТЕРЫ ПРИВЛЕКАТЕЛЬНЫ). Возможно, нам захочется выбросить первый элемент списка, оставив (COMPUTERS ARE NICE), или же мы захотим добавить новый элемент BIG, т. е.

БОЛЬШИЕ, и получить что-то вроде (BIG FAST COMPUTERS ARE, NICE). Настал момент рассмотреть преобразования такого рода, отправляясь от основных методов разделения списков на части. В частности, нам следует понять работу функций CAR.CDR, APPEND и C.ONS. К сожалению, исторически сложившиеся обозначения таковы, что первые три из этих функций не имеют никакого мнемонического содержания - их просто следует запомнить*.

* (Четвертая из этих основных функций, однако, названа первыми четырьмя буквами английского CONSTRUCT, т. е. конструировать.- Прим. перев)

Некоторые примеры помогут объяснить то, как действуют эти основные функции CAR, CDR, APPEND, CONS. Не обращайте внимания на одиночную кавычку, которая появляется в примерах. Ее смысл будет объяснен очень скоро.

Теперь за работу. Функция CAR возвращает первый элемент списка:

 (CAR '(FAST COMPUTERS ARE NICE)) 
    FAST 
 (CAR '(А В С)) 
    A 
 (CAR '((A B)C)) 
   (A B) 

Заметим, что в последнем примере аргументом для CAR является двухэлементный список ((А В) С). Первый элемент сам по себе является списком, (А В), и представляет собой первый элемент аргумента. Поэтому функция CAR возвращает (А В).

Функция CDR осуществляет дополнительную операцию. Она действует над списком и возвращает то, что остается после удаления первого элемента списка.

(CDR '(FAST COMPUTERS ARE NICE)) 
    (COMPUTERS ARE NICE) 
(CDR '(ABC) 
    (B C) 
(CDR '((AB)C)) 
    (C) 

Вычисление значения (оценивание) часто умышленно подавляется с помощью одиночной кавычки

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

 (CAR (CDR (А В С))) 

Возникает, однако, трудность. Мы хотели бы, чтобы функция CDR, восприняв (ABC), выдала (В С). В этом случае CAR, безусловно, вернет нам В, т. е. второй элемент первоначального списка. Но как система Лисп может узнать, где кончаются указания на то, что следует проделать, и начинаются те данные, которые подвергаются обработке? Встретив вложенный список

(А В С)  

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

(CDR (А В С)) 

представляет собой, несомненно, список, первым элементом которого является CDR. Таким образом, выражение

 (CAR (CDR (А В С))) 

вполне может своим результатом иметь CDR!

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

 (CAR (CDR '(А В С))) 

возвращает В, поскольку знак кавычки не дает системе Лисп забраться в (А В С) и рассматривать его как s-выражение, в котором функция А применяется к В и С. Вместо этого (А В С) поступает в качестве аргумента к функции CAR, которая затем передает (В С) CDR, что окончательно приводит просто к В*.

* (Во многих реализациях Лиспа наряду с кавычкой (иногда вместо нее) используется функция QUOTE одного аргумента. Тогда, например, (CAR (QUOTE (GDR (ABC)))) полностью эквивалентно (CAR' (CDR (ABC))).- Прим. перев.)

Перемещение кавычки приводит к изменению результата. Если мы наберем на терминале (CAR' (CDR (А В С))), то система Лисп не будет брать CDR от чего-либо, а просто передаст s-выражение (CDR (А В С)) функции CAR как список. Результатом будет CDR, поскольку CDR первый элемент списка.

Если знак кавычки вообще будет опущен, то это приведет к попытке рассматривать А как функцию. Такой функции в самой системе Лисп нет и если таковая не была определена пользователем, то система запнется и сообщит об ошибке, связанной с неопределенной функцией.

  • Важно знать, что область действия "механизма кавычки" в точности распространяется на идущее за ней s-выражение. Кавычка, стоящая перед списком, предотвращает всякую попытку расценивать список как некую операцию.
  • Не существует никакого механизма снятия кавычки. Это значит, что нет способа включения процесса оценивания где-нибудь внутри списка, отмеченного кавычкой.

Композиция функций CAR и CDR облегчает программирование

Когда для выбора какого-то глубоко вложенного элемента внутри s-выражения требуется много функций CAR и CDR, обычно удобно пользоваться составной функцией вида C-R,C--R,C---R или С R. Каждый - есть либо А, обозначая CAR, либо D, обозначая CDR. Так выражение (CADR '(А ВС)) полностью эквивалентно (CAR(CDR '(А В С))).

предыдущая главасодержаниеследующая глава

https://se.video/watch/50431/ samsung sam








© Злыгостев А.С., 2001-2019
При использовании материалов сайта активная ссылка обязательна:
http://informaticslib.ru/ 'Библиотека по информатике'
Рейтинг@Mail.ru
Поможем с курсовой, контрольной, дипломной
1500+ квалифицированных специалистов готовы вам помочь