Новосибирский Государственный Университет
Опубликован: 05.02.2007 | Доступ: свободный | Студентов: 2222 / 413 | Оценка: 4.30 / 4.23 | Длительность: 10:15:00
Лекция 8:

Имена и контексты

< Лекция 7 || Лекция 8: 123 || Лекция 9 >
Аннотация: Данная лекция посвящена организации эффективной работы с определениями функций. Рассматривается оптимизационная техника именования значений и подвыражений на разных уровнях вложенности блоков, представление переменных и констант, а также локализация функций по блокам

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

Задача: Пусть множества представлены с помощью списков. Для начала рассмотрим простые множества, элементами которых могут быть только атомы. Надо реализовать объединение ( UNION ) и пересечение ( INTERSECTION ) множеств.

Предварительный анализ задачи:

Функции UNION и INTERSECTION применяют к множествам, каждое множество представлено в виде списка атомов. Заметим, что обе функции рекурсивны и используют вспомогательную функцию, выясняющую входит ли атом в список (MEMBER).

Работу этих функций можно выразить следующим образом:

MEMBER – это функция двух аргументов, первый аргумент "А" - атом, а второй аргументсписок "Х". Функция вырабатывает значение "Т", если "А" входит в список "Х".

Алгоритм:

Определение тела функции состоит из трех ветвей:

  • Если второй аргумент – пустой список,

    то значение функции Nil, т.е. атом в списке не найден.

  • Иначе если атом "А" совпадает с "головой" второго аргумента,

    то значение функции T, т.е. атом имеется в списке.

  • Иначе продолжаем поиск в "хвосте" списке, т.е. рекурсивно применяем исходную функцию к редуцированному второму аргументу.
алг member ( атом  a, список x) арг a, x
  нач 
       если  пусто (a)
       то знач := Nil
       инес равно (a, голова (x) )
       то знач := T
       иначе   знач := member (a, хвост (x))
  кон

UNION – это функция двух аргументов, оба аргумента "X" и "Y" - списки, представляющие множества. Функция вырабатывает новый список, в который входят все атомы из списков "Х" и "Y".

Алгоритм:

Определение тела функции состоит из трех ветвей:

  • Если первый аргумент – пустой список,

    то значением является второй аргумент, т.е. можно ничего не строить.

  • Иначе если "голова" первого аргумента входит во второй аргумент,

    то достаточно объединить хвост первого аргумента со вторым аргументом, т.е. рекурсивно применяем исходную функцию, редуцируя первый аргумент.

  • Иначе "голову" первого аргумента присоединяем к результату объединения редуцированного первого аргумента со вторым аргументом.
алг UNION (список x,y) арг  x, y
  нач 
       если  пусто (x)
       то знач := y
       инес member ( голова (x), y )
       то знач := UNION (хвост (x), y)
       иначе   знач := cons (голова (x), UNION (хвост (x), y))
  кон

INTERSECTION – это функция двух аргументов, оба аргумента "X" и "Y" - списки, представляющие множества. Функция вырабатывает новый список, в который входят атомы списка "Х", входящие в список "Y".

Алгоритм:

Определение тела функции состоит из трех ветвей:

  • Если первый аргумент – пустой список,

    то и пересечение - пустой список.

  • Иначе если "голова" первого аргумента входит во второй аргумент,

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

  • Иначе применяем пересечение к редуцированному первому аргументу со вторым аргументом.
алг INTERSECTION (список x,y) арг  x, y
  нач 
       если  пусто (x)
       то знач := Nil
       инес member ( голова (x), y )
       то знач := cons (голова (x), INTERSECTION (хвост (x), y))
       иначе   знач := INTERSECTION (хвост (x), y)
  кон

Определяя эти функции на Лиспе, мы используем специальную псевдо-функцию DEFUN. Программа выглядит так:

(DEFUN  MEMBER (A X)
;определение проверки входит ли атом в список
    (COND 
      ((NULL X)  Nil)  
      ((EQ A (CAR X)) T)
      (T    (MEMBER A (CDR X)) )
)   ) 

(DEFUN UNION (X Y)
;определение объединения двух множеств
   (COND
     ((NULL X) Y)
     ((MEMBER (CAR X) Y) (UNION (CDR X) Y) )
     (T (CONS (CAR X) (UNION (CDR X) Y))) )) )  
))  

(DEFUN INTERSECTION (X Y)
;определение пересечения двух множеств
     (COND
     ((NULL X) NIL)  
     ((MEMBER (CAR X) Y) (CONS (CAR X) (INTERSECTION (CDR X) Y)) )
     (T (INTERSECTION (CDR X) Y))  
))  
  
(INTERSECTION '(A1 A2 A3) '(Al A3 A5))  
;тест на пересечение двух множеств
(UNION '(X Y Z) '(U V W X)) 
;тест на объединение двух множеств

Эта программа предлагает Лисп-системе вычислить пять различных форм. Первые три формы сводятся к применению псевдо-функции DEFUN. Значение четвертой формы - (A1 A3). Значение пятой формы - (Y Z C B D X). Анализ пути, по которому выполняется рекурсия, показывает, почему элементы множества появляются именно в таком порядке.

Псевдо-функция - это функция, которая выполняется ради ее воздействия на систему, тогда как обычная функция - ради ее значения. DEFUN заставляет функции стать определенными и допустимыми в системе равноправно со встроенными функциями. Ее значение - имя определяемой функции, в данном случае - MEMBER, UNION, INTERSECTION. Можно сказать более точно, что полная область значения псевдо-функции DEFUN включает в себя некоторые доступные ей части системы, обеспечивающие хранение информации о функциональных объектах, а формальное ее значениеатом, символизирующий определение функции.

В этом примере продемонстрировано несколько элементарных правил написания функциональных программ, выбранных при реализации интерпретатора Лисп 1.5 в дополнение к идеализированным правилам, сформулированным в строгой теории Лиспа, которая описана в предыдущем разделе.

  1. Программа состоит из последовательности вычисляемых форм. Если форма список, то ее первый элемент интерпретируется как функция. Остальные элементы списка – аргументы для этой функции. Они вычисляются с помощью EVAL, а функция применяется к ним с помощью APPLY и полученное значение выводится как результат программы.
  2. Нет особого формата для записи программ. Границы строк игнорируются. Формат программы, включая идентификацию, выбран просто для удобства чтения.
  3. Любое число пробелов и концов строк можно разместить в любой точке программы, но не внутри атома.
  4. Не используются (QUOTE T) и (QUOTE NIL). Вместо них используется T и NIL, что влечет соответствующее изменение определения EVAL.
  5. Атомы должны начинаться с букв, чтобы легко отличаться от чисел.
  6. Точечная нотация может быть привлечена наряду со списочной записью. Любое число пробелов перед или после точки, кроме одного, будет игнорироваться (один пробел обязательно нужен).
  7. Точечные пары могут появляться как элементы списка, и списки могут быть элементами точечных пар.

Например:

((A . B) X (C . (E F D))) - есть допустимое S-выражение.

Оно может быть записано как

((A . B) . ( X . ((C . (E . ( F . (D . Nil))) ) . Nil)))

или

((A . B) X (C E F D))
  1. Форма типа (A B C . D) есть сокращение для (A . ( B . ( C . D) )). Любая другая расстановка запятых или точек на одном уровне есть ошибка, например, (A. B C) или (A B . C D) не соответствуют никакой структуре данных. (Реализационное расширение списочной записи. " . D " здесь означает, что вместо Nil, по умолчанию завершающего список, в данной структуре размещен атом " D ")
  2. Набор основных функций обеспечен системой. Другие функции могут быть введены программистом. Любая функция может использоваться в определении другой функции с учетом иерархии построений.

При наборе форм в диалоге интерпретатор сам напечатает результаты, а при загрузке программы их файла надо позаботиться о выводе результатов программы с помощью псевдо-функции PRINT.

(PRINT (INTERSECTION '(A1 A2 A3) '(Al A3 A5))  )
(PRINT (UNION '(X Y Z) '(U V W X)) )
(PRINT (UNION (READ) '(1 2 3 4)) )      
; объединение вводимого списка со списком '(1 2 3 4)
< Лекция 7 || Лекция 8: 123 || Лекция 9 >