Шаг 58.
Управляющие конструкции. Организация циклов

    На этом шаге мы рассмотрим способы организации циклов.

    В числовых вычислениях существует тенденция максимально использовать итерационные методы (в численном анализе усилия в основном направлены на изучение и использование алгоритмов, основанных на итерациях). Для "символьных вычислений" естественным является использование рекурсивных методов, которые намного лучше подходят для обработки структур символьной информации. Однако ясно, что различие между числовыми и символьными вычислениями до некоторой степени искусственно.

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

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

   (LOOP
       Expr1
       Expr2
        ...
       ExprN
   )

где в качестве аргументов могут быть использованы любые S-выражения либо специальные конструкции вида ( C E1 E2 ... Ek ), где C,E1,E2,...,Ek - S-выражения.

    В последнем случае список используется в качестве точки анализа условия окончания цикла. Если вычисление S-выражения C возвращает значение NIL, итерационный процесс продолжается, в противном случае прекращается, но предварительно вычисляются последовательно S-выражения E1, E2, ..., Ek и последнее из полученных значений возвращается в качестве значения функции LOOP.


    Пример 1. Функция FACTORIAL возвращает факториал числа NUM в переменной ANS.
   ; ------------------------------------------- ;
   ; Пример императивного стиля программирования ;
   ; ------------------------------------------- ;
   (DEFUN FACTORIAL (LAMBDA (NUM ANS)
       ( (NOT (> NUM -1)) NIL )
       (SETQ ANS 1)
       (LOOP
           ( (EQ NUM 0) ANS )
           (SETQ ANS (* NUM ANS))
           (SETQ NUM (- NUM 1))
       )
   ))
   ; --------------------------------------------- ;
   ; Пример функционального стиля программирования ;
   ; --------------------------------------------- ;
   (DEFUN FACT (LAMBDA (X)
       (COND ( (ZEROP X) 1 )
             (  T  (* X (FACT (- X 1))) )
       )
   ))
Текст этих функций можно взять здесь.


    Пример 2. Функция NTH возвращает список, полученный в результате удаления первых NUM элементов из списка LST.
   ; ------------------------------------------- ;
   ; Пример императивного стиля программирования ;
   ; ------------------------------------------- ;
   (DEFUN NTH (LAMBDA (LST NUM)
       ( (NOT (PLUSP NUM)) LST )
       (LOOP
           (SETQ LST (CDR LST))
           (SETQ NUM (- NUM 1))
           ( (ZEROP NUM) LST)
       )
   ))
   ; --------------------------------------------- ;
   ; Пример функционального стиля программирования ;
   ; --------------------------------------------- ;
   (DEFUN NTH_1 (LAMBDA (LST NUM)
      (COND ( (ZEROP NUM) LST )
            (  T  (NTH_1 (CDR LST) (- NUM 1)) )
      )
   ))
Текст этих функций можно взять здесь.


    Пример 3. Предикат, позволяющий установить, является ли заданное целое число N простым.
   ; ------------------------------------------- ;
   ; Пример императивного стиля программирования ;
   ; ------------------------------------------- ;
   (DEFUN SIMPLE (LAMBDA (N)
      (SETQ FLAG 1)
      (COND ( (AND (NOT (ODDP N)) (NOT (EQ N 2))) NIL )
            (  T  ( (SETQ I 3)
                    (LOOP
                       ( (< (CAR (DIVIDE N 2)) I) )
                       ( (EQ (REM N I) 0)
                                    (SETQ FLAG 0) )
                       ( SETQ I (+ I 2) )
                    )
                    (COND ( (EQ FLAG 0) NIL )
                          (   T          T  ))) )
      )
   ))
   ; --------------------------------------------- ;
   ; Пример функционального стиля программирования ;
   ; --------------------------------------------- ;
   (DEFUN ISPRIM (LAMBDA (N)
      (COND ( (EQ N 1) NIL )
            (    T    (ISPR N 2) )
      )
   ))
   ; --------------------- ;
   (DEFUN ISPR (LAMBDA (N M)
      (COND ( (EQ M N)                T  )
            ( (EQ (REM N M) 0) NIL )
            (           T            (ISPR N (+ M 1)) )
      )
   ))
   ; --------------------------------------------- ;
   ; Пример функционального стиля программирования ;
   ;               ("быстрый" алгоритм)            ;
   ; --------------------------------------------- ;
   (DEFUN ISPRIM1 (LAMBDA (N)
      (COND ( (EQ N 1)     NIL    )
            (    T    (ISPR1 N 2) )
      )
   ))
   ; ---------------------- ;
   (DEFUN ISPR1 (LAMBDA (N M)
      (COND ( (> (* M M) N)  T  )
            ( (EQ (REM N M) 0)   NIL )
            (           T             (ISPR1 N (+ M 1)) )
      )
   ))
Текст этих функций можно взять здесь.


    Замечание. В версии muLISP85 есть следующие функции, облегчающие поддержку стиля императивного программирования: SYSTEM, IF, IDENTITY, COMMENT, RETURN, RESTART, EXECUTE.

    1. Функция (SYSTEM) закрывает все открытые файлы, заканчивает выполнение muLISP и возвращает управление операционной системе.

    Если система muLISP85 работает под управлением MS-DOS, а также если INT есть нуль или положительное целое число меньшее, чем 256, функция  (SYSTEM INT) возвращает код выхода (называемый еще кодом возврата) INT для управляющего процесса. Код выхода может запрашиваться командой MS-DOS "IF ERRORLEVEN".

    Если INT отсутствует, то система muLISP возвращает код возврата, по умолчанию хранящийся в странице памяти muLISP.

    2. Функция (RESTART) закрывает все открытые файлы, "отказывается" от текущей среды muLISP и инициирует новую систему muLISP. Все связи между переменными, функции пользователей и значения свойств в текущей среде разрушаются.

    3. Функция (COMMENT COMMENTS) "игнорирует" свои аргументы и возвращает NIL.

    Функция COMMENT определяет способ включения комментариев непосредственно в определение функции. Такие комментарии используют области памяти в RAM-памяти.

    В противоположность этому, комментарии, ограниченные макро-символами комментариев, игнорируются при считывании в muLISP. Хотя такие комментарии занимают место в исходном файле на диске, они не используют области памяти в RAM-памяти.

    4. Функция (IDENTITY OBJECT) возвращает OBJECT без каких-либо других действий. Предикат неявной COND должет быть применением функции, а не только переменной. Следовательно, IDENTITY может применяться для использования переменных в качестве предикатов. Например:

   $ (IDENTITY NIL)    $ (IDENTITY '(A B C))
   NIL                 (A B C)
   $ (IDENTITY 'DOG)
   DOG

    С помощью функции IDENTITY построим функцию TEST-VAR:

   (DEFUN TEST-VAR (VAR)
      ( (IDENTITY VAR) 'VAR-IS-TRUE) 'VAR-IS-FALSE
   )
   $ (TEST-VAR NIL)
   VAR-IS-FALSE

    Приведем код функции IDENTITY:

   (DEFUN IDENTITY (OBJ)
      OBJ
   )

    5. Если значением предиката PREDICATE не является NIL, то функция (IF PREDICATE THEN ELSE) вычисляется, и возвращается значение THEN. В противном случае вычисляется и возвращается ELSE или NIL, если ELSE отсутствует.

    Функция IF в версии muLISP85 аналогична конструкции "if-then-else" в языках императивного программирования. Например:

   $ (IF (EQ (* 2 3) 6) 'TRUE 'FALSE)
   TRUE
   $ (IF (EQ (+ 2 3) 6) 'TRUE 'FALSE)
   FALSE

    Код функции может быть записан так:

   (DEFUN IF (NLAMBDA (PREDICATE THEN ELSE)
      ( (EVAL PREDICATE) (EVAL THEN) )
      ( EVAL ELSE )
   ))

    6. Функция (RETURN OBJECT) приостанавливает выполнение функции, содержащей RETURN, очищает стеки и возвращает значение обьекта OBJECT в качестве своего значения. Например:

   (DEFUN return-test ()
      (PRINT 'DOG) (RETURN 'CAT) (PRINT 'PIG)
   )
   (return-test)
   DOG
   CAT

    7. Функция (EXECUTE PROGRAM COMMAND-LINE) приостанавливает muLISP, затем загружает и выполняет программу PROGRAM, передавая ей в качестве аргумента строку команд COMMAND-LINE.

    Когда программа PROGRAM завершается, то управление возвращается к muLISP, а функция EXECUTE возвращает код выхода из программы PROGRAM.

    Если программа PROGRAM не найдена, то функция EXECUTE возвращает NIL.

    Наиболее часто функция EXECUTE используется для вызова вторичного процессора команд. Например, если файл процессора команд COMMAND.COM размещается на устройстве А:, то команда muLISP

   (EXECUTE "A: COMMAND.COM" "/C DIR B:")

покажет содержимое каталога файлов на устройстве В.

    Со следующего шага мы начнем рассматривать фундаментальные типы данных.




Предыдущий шаг Содержание Следующий шаг