На этом шаге мы приведем некоторые общие сведения по отладке программ.
Приведем несколько тестовых примеров для нашего интерпретатора: они, к сожалению (!), не позволили найти ошибки:
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 с другими языками.