На этом шаге мы приведем некоторые общие сведения по отладке программ.
Приведем несколько тестовых примеров для нашего интерпретатора: они, к сожалению (!), не позволили найти ошибки:
1. <-- ((NLAMBDA1 (X) (CDR1 X)) (1 (TIMES1 1 2) 4))
--> ((TIMES1 1 2) 4)
<-- ((NLAMBDA1 (X) (CAR1 X)) ((TIMES1 1 2) (PLUS1 1 4)))
--> (TIMES 1 2)
2. <-- (DEFUN1 SUMMA (LAMBDA1 (LST) (COND1 ((EQUAL1 LST
NIL1) 0) (T1 (PLUS1 (CAR1 LST) (SUMMA (CDR1 LST))))))
--> (LAMBDA1 (LST) (COND1 ((EQUAL1 LST NIL1) 0) (T1
(PLUS1 (CAR1 LST) (SUMMA (CDR1 LST)))))
<-- (SUMMA (QUOTE1 (1 2 3)))
--> 6
3. <-- (DEFUN1 FACT (LAMBDA1 (X) (COND1 ((EQUAL1 X 1) 1)
(T1 (TIMES1 X (FACT (DIFFERENCE1 X 1)))))))
--> (LAMBDA1 (X) (COND1 ((EQUAL1 X 1) 1) (T1 (TIMES1
X (FACT (DIFFERENCE1 X 1))))))
<-- (FACT 5)
--> 120
4. <-- (DEFUN1 TEST (LAMBDA1 (X Y) (CONS1 X Y)))
--> (LAMBDA1 (X Y) (CONS1 X Y))
<-- (TEST A B)
--> (A . B)
5. <-- (DEFUN1 COPY (LAMBDA1 (E) (COND1 ((ATOM1 E) E) (T1
(CONS1 (COPY (CAR1 E)) (COPY (CDR1 E)))))))
--> (LAMBDA1 (E) (COND1 ((ATOM1 E) E) (T (CONS1 (COPY
(CAR1 E)) (COPY (CDR1 E))))))
<-- (COPY (QUOTE1 (A B C)))
--> (A B C)
6. <-- (DEFMACRO1 AAA (NLAMBDA1 (G) (CAR1 G)))
--> (NLAMBDA1 (G) (CAR1 G))
<-- (AAA ((TIMES1 2 3) 4 5))
--> (TIMES1 2 3)
Например, определим функцию NULL1 через примитивы системы:
<-- (DEFUN1 NULL1 (LAMBDA1 (L) (EQUAL1 L NIL1))) --> (LAMBDA1 (L) (EQUAL1 L NIL1))
7. <-- (DEFUN1 MEMBER1 (LAMBDA1 (AL LST) (COND1 ((NULL1
LST) NIL1) ((EQUAL1 AL (CAR1 LST)) LST) (T1 (MEMBER1
AL (CDR1 LST))))))
--> (LAMBDA1 (AL LST) (COND1 ((NULL1 LST) NIL1)
((EQUAL1 AL (CAR1 LST)) LST) (T1 (MEMBER1 AL (CDR1
LST)))))
<-- (MEMBER1 (QUOTE1 B) (QUOTE1 (A B C)))
--> (B C)
8. <-- (DEFUN1 DEL (LAMBDA1 (A L) (COND1 ((NULL1 L) NIL1)
((EQUAL1 (CAR1 L) A) (CDR1 L)) (T1 (CONS1 (CAR1 L)
(DEL A (CDR1 L)))))))
--> (LAMBDA1 (A L) (COND1 ((NULL1 L) NIL1) ((EQUAL1
(CAR1 L) A) (CDR1 L)) (T1 (CONS1 (CAR1 L) (DEL A (CDR1
L)))))
<-- (DEL 5 (QUOTE1 (3 4 5)))
--> (3 4)
9. <-- (DEFUN1 EVERY (LAMBDA1 (L) (COND1 ((NULL1 L) NIL1)
((NULL1 (CDR1 L)) L) (T1 (CONS1 (CAR1 L) (EVERY (CDR1
(CDR1 L))))))))
--> (LAMBDA1 (L) (COND1 ((NULL1 L) NIL1) ((NULL1 (CDR1
L)) L) (T1 (CONS1 (CAR1 L) (EVERY (CDR1 (CDR1 L)))))))
<-- (EVERY (QUOTE1 (A P R O L D)))
(A R L)
10. <-- (DEFUN1 APPEND1 (LAMBDA1 (L1 L2) (COND1 ((NULL1 L1)
L2) ( T1 (CONS1 (CAR1 L1) (APPEND1 (CDR1 L1) L2)))))))
--> (LAMBDA1 (L1 L2) (COND1 ((NULL1 L1) L2) (T1 (CONS1
(CAR1 L1) (APPEND1 (CDR1 L1) L2))))))
<-- (APPEND1 (QUOTE1 (A P R)) (QUOTE1 (O L D)))
(A P R O L D)
После проверки Вас может заинтересовать три вопроса:
На третий вопрос ответ таков: при выборе этих тестов мы не руководствовались никакими теоретическими положениями, это были первые пришедшие на ум функции. В связи с этим Вам дается задание: установить недостатки предложенной системы тестов и улучшить ее.
На первые два вопроса мы ответим ниже (см. более подробное изложение в [1]).
Будем говорить, что "в программе имеется ошибка, если ее выполнение не оправдывает ожиданий пользователя" [2, 3].
Напомним, что при решении задач с использованием компьютера под отладкой программ понимается обычно один из этапов решения, во время которого с помощью компьютера происходит обнаружение и исправление ошибок, имеющихся в программе; в ходе отладки программист хочет добиться определенной степени уверенности в том, что его программа соответствует своему назначению и не делает того, для чего она не предназначена.
Составные части процесса отладки можно схематически изобразить в "форме Бэкуса-Наура":
¦ Процесс ¦ ¦ Процесс ¦
[Процесс отладки] ::= ¦ поиска ¦ ¦ исправления ¦
¦ ошибки ¦ ¦ ошибки ¦
¦ Процесс ¦ ¦ Процесс ¦ ¦ Процесс ¦
¦ поиска ¦ ::= ¦ тестирования ¦ ¦ локализации ¦
¦ ошибки ¦ ¦ программы ¦ ¦ ошибки ¦
Начинающий программист, как правило, переоценивает свои возможности и, разрабатывая программу, исходит из того, что в его программе ошибок не будет. А говоря про только что составленную программу, готов уверять, что она на 99% правильна, и ее остается только для большей уверенности один(!) раз выполнить на компьютере с какими-нибудь(!) исходными данными. Естественно, что каждый неверный результат, каждая найденная ошибка вызывают у него изумление и считаются, конечно, последними. Вследствие такого подхода получение с помощью компьютера надежных результатов по составленной программе откладывается на длительный и неопределенный срок.
Оказывается, что практически невозможно для достаточно сложной программы быстро найти и устранить все имеющиеся в ней ошибки. Трудности программирования и отладки подчеркивает следующий популярный в среде программистов афоризм: "В каждой программе есть по крайней мере одна ошибка". Поэтому можно сказать, что наличие ошибок в только что разработанной программе - вполне нормальное и закономерное явление. А совсем ненормальным, из ряда вон выходящим фактом является отсутствие ошибок в программе, которая не была еще подвергнута тщательному тестированию и отладке (конечно, речь здесь идет о достаточно сложных программах!).
Поэтому разумно уже при разработке программы на этапах алгоритмизации и программирования готовиться к обнаружению ошибок на стадии отладки и принимать профилактические меры для их предупреждения.
Более подробную информацию по отладке программ можно получить в разделе "Отладка".
Со следующего шага мы начнем рассматривать связь языка программирования LISP с другими языками.