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

Стандартное программирование

< Лекция 10 || Лекция 11: 12 || Лекция 12 >

Prog-форма может быть рекурсивной.

Лисп Паскаль
(DEFUN rev (x)
              (prog (y z)
       
A  (COND ((null x)(return y)))
       (setq z (CDR x))
 
  (COND ((ATOM z)(goto B)))
       (setq z (rev z)) 

B    (setq y (CONS z y))
       (setq x (CDR x))
           (goto A)
))
function rev (x: list) :List
       var y, z: list;
begin
 A: if null (x) Then rev := y;
          z := cdr (x);

    if atom (z) then goto B;
          z := rev (z);

 B:   y := cons (z, y);
       x := cdr (x);
                    goto A
end;

11.1.
                    Функция REV, обращающая список и все подсписки, столь же естественно пишется с помощью рекурсивной Prog-формы.

Функция rev обращает все уровни списка, так что rev от (A ((B C) D)) даст ((D (C B))A).

Для того, чтобы форма prog была полностью законна, необходима возможность дополнять ассоциативный список рабочими переменными. Кроме того операторы этой формы требуют специального расширения языка - в него включаются формы go, set и return, не известные вне prog. (Формы Go, Set, Return работают как операторы лишь на верхнем уровне PROG или внутри COND, находящегося на верхнем уровне PROG. Но в современных версиях Лиспа их можно встретить и в других позициях.)

Атомы, выполняющие роль меток, работают как указатели помеченного блока.

Кроме того произошло уточнение механизма условных выражений, - отсутствие истинного предиката не препятствует формированию значения cond-оператора, т.к. все операторы игнорируют выработанное значение. Это позволяет считать, что при отсутствии истинного предиката значением условного выражения является Nil. Такое доопределение условного выражения давно перекочевало и в области обычных функций, где часто дает компактные формулы для рекурсии по списку. Исчезает необходимость в ветви вида " (T NIL) " .

В принципе SET и SETQ могут быть реализованы с помощью a-списка примерно также как и доступ к значению аргумента, только с копированием связей, расположенных ранее изменяемой переменной (см. функцию assign из параграфа 4). Более эффективная реализация, на основе списков свойств, будет описана ниже.

(DEFUN set (x y) (assign  x  y  Alist))

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

(setq x 'y)
  (set x 'NEW)
  (print x)
  (print y)
11.2. Побочный эффект присваиваний с вычисляемой левой частью

Напечатается Y и NEW.

Циклы

Работа с циклами обеспечена в Лиспе достаточно традиционно.

(loop <форма>...)

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

(do(<параметры>...)(<предикат > < результат >...)
                    < форма >...)     
(do*(<параметры >...)(<предикат > < результат >...)
                    < форма >...)

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

(dolist (<переменная > < список > [<результат >] )
                   < форма >...)

Цикл, перебирающий список выражений, поочередно присваиваемых переменной цикла.

(dotimes (<переменная > < число > [<результат >] ) 
                   < форма >...)

Цикл, работающий заданное число шагов от 0 до N-1

< параметры > задаются как списки вида 

 (<переменная> <начальное_значение> [<шаг>] )
   в котором:
    < переменная > - символ с исходным значением Nil.
    < начальное_значение > - начальное значение параметра.
    < шаг > - выражение для вычисления параметра на каждом шаге цикла 
    <предикат>  - ограничитель цикла
    <результат> - результирующее выражение (при отсутствии - NIL)
     <форма> - тело цикла, работает как неявная форма prog.

Значение дает последнее результирующее выражение.

Примеры программ с циклами

(defun first-a (la) 
;; самый левый атом
      (setq x la)
   (loop 
       (setq x (car x))   ; левый элемент структуры
       (cond ((atom x)(return x)) )   
	 ; явный выход из цикла при обнаружении атома
)  )                                        

(print (first-a '(((123) 46) 5) ))
11.3. Выбор самого левого атома из произвольной структуры данных
(defun len-do (ld)
;; длина списка
(do
       ((x ld (cdr x)); на каждом шаге переход к хвосту списка
        (N 0 (1+ N))) ;  подсчет числа шагов
 ((null x) N))        ; выход из цикла при пустом списке
)

(print (len-do '(1 2 3 4 5)))
11.4. Вычисление длины списка
(defun list-pa (lp) 
         (setq rl nil)
     (dolist 
      (el lp rl)  ; параметры перебора и результат
         (setq rl (cons (cons el el) rl))
))

(print (list-pa '(a b c d)))    ; = ((A . A)(B . B)(C . C)(D . D))
11.5.
(defun ind-n (ln n)
         (setq bl ln) 
         (setq ind nil)
(dotimes (i n ind) 
   (setq ind (cons (- n i) (cons (car bl ) ind )))
   (setq bl (cdr bl))
))
(print (ind-n '(a b c d e f g) 4))  ; = D
11.6. Индексный выбор элемента из списка
Таблица 11.1. Clisp: Функции, моделирующие императивный стиль
(Go Атом ) Безусловный переход на оператор, помеченный Атомом
(Prog Атомы-или-Формы …) Вычисляет последовательность форм в императивном стиле
(Prog1 Форма …) Вычисляет формы, формальный результат – значение первой из них.
(Prog2 Форма …) Вычисляет формы, формальный результат – значение второй из них.
(Progn Форма …) Вычисляет формы, формальный результат – значение последней из них.
(Return Форма ) Результат и завершение формы Prog
(Do (var ...) ( expr rez ...) expr ...) Цикл с последовательным заданием параметров и выходом по заданному условию
(Do* (var ...) ( expr rez ...) expr ...) Цикл с параллельным заданием параметров и выходом по заданному условию
(Dolist (var list [rez] ) expr ...) Цикл перебора значений параметра из списка
(Dotimes (var number [rez] ) expr ...) Цикл, работающий заданное число раз
(Loop expr ...) Цикл работающий до внутреннего Return

Выводы:

  • При реализации языка программирования происходит его расширение с целью повышения практичности программирования.
  • Стандартные, операторно-процедурные построения моделируются с помощью функций.
< Лекция 10 || Лекция 11: 12 || Лекция 12 >