|
52 | 52 | ◊indexR{переменные!связь с~символами}
|
53 | 53 | ◊indexR{символы!связь с~переменными}
|
54 | 54 | Как видим, наш интерпретатор неявно преобразует символы в~переменные.
|
55 |
| -Если быть более дотошным, тогда вместо ◊ic{(lookup exp env)} надо записать что-то вроде: |
| 55 | +Если быть более дотошным, то вместо ◊ic{(lookup exp env)} надо записать что-то вроде: |
56 | 56 |
|
57 | 57 | ◊code:lisp{
|
58 | 58 | ... (lookup (symbol->variable exp) env) ...
|
59 | 59 | }
|
60 | 60 |
|
61 |
| -◊; продолжать здесь: |
62 |
| - |
63 | 61 | ◊indexR{приведение типов}
|
64 | 62 | ◊indexR{объекты!второго класса}
|
65 |
| -В~таком случае мы явно говорим, |
66 |
| -что символ~◊ic{exp}, чьим значением является имя переменной, должен быть превращён в~переменную. |
| 63 | +В~таком случае мы явно говорим, что символ~◊ic{exp}, чьим значением является имя переменной, должен быть преобразован в~переменную. |
67 | 64 | Также это подчёркивает тот факт, что функция ◊ic{symbol->variable}
|
68 | 65 | ◊footnote{Лично я не~люблю называть функции приведения типов ◊nobr{◊ic{◊ii{x}->◊ii{y}}},
|
69 | 66 | потому что так сложнее понимать цепочки преобразований.
|
70 |
| -Запись ◊ic{(◊ii{y}"~>◊ii{z}(◊ii{x}"~>◊ii{y} ...))} не~так очевидна, как ◊ic{(◊ii{z}<"~◊ii{y}(◊ii{y}<"~◊ii{x} ...))}. |
71 |
| -Хотя, с~другой стороны, одиночная запись ◊ic{◊ii{z}<"~◊ii{y}} не~так легко читается, как ◊ic{◊ii{y}"~>◊ii{z}}. |
| 67 | +Запись ◊nobr{◊ic{(◊ii{y}->◊ii{z}(◊ii{x}->◊ii{y} ...))}} не~так очевидна, как ◊nobr{◊ic{(◊ii{z}<-◊ii{y}(◊ii{y}<-◊ii{x} ...))}}. |
| 68 | +Хотя, с~другой стороны, одиночная запись ◊nobr{◊ic{◊ii{z}<-◊ii{y}}} не~так легко читается, как ◊nobr{◊ic{◊ii{y}->◊ii{z}}}. |
72 | 69 | Приходится~выбирать.}
|
73 |
| -вовсе не~переводит ◊ic{exp} сам в~себя; |
74 |
| -она превращает синтаксическую сущность (символ) в~семантическую (переменную). |
75 |
| -В~действительности, переменные — это лишь воображаемые объекты, которым язык и программист дали какие-то имена, |
76 |
| -и которые ради удобства используются в~форме имён. |
77 |
| -Способ представления имён также выбран из соображений удобства, так как Лисп имеет базовый тип символов. |
78 |
| -В~данном случае |
79 |
| -◊ic{symbol->variable} ничего не~делает, хотя на самом деле могла бы применяться |
80 |
| -какая-нибудь другая форма записи имени переменной, например: |
81 |
| -строка, состоящая из знака доллара и имени переменной; в~этом случае, конечно~же, ◊ic{symbol->variable} будет сложнее. |
82 |
| - |
83 |
| -Если бы переменные действительно были лишь воображаемыми, то ◊ic{lookup} |
84 |
| -не~знала~бы как обрабатывать свой первый аргумент, так как она ожидает нечто |
85 |
| -«материальное». Так что нам надо преобразовать переменную в~её программное |
86 |
| -представление, какой-нибудь уникальный ключ, по которому ◊ic{lookup} сможет |
87 |
| -отыскать переменную в~окружении. Так что ещё точнее было~бы записать: |
| 70 | +вовсе не~переводит символ~◊ic{exp} сам в~себя; |
| 71 | +наоборот, она превращает синтаксическую сущность (символ) — в~семантическую (переменную). |
| 72 | +Имена переменных существуют лишь в~языке программирования для удобства программиста; |
| 73 | +если переменную везде переименовать, то поведение программы не~изменится. |
| 74 | +Способ представления имён выбран из соображений удобства, так как Лисп имеет базовый тип символов. |
| 75 | +В~нашем случае ◊ic{symbol->variable} ничего не~делает, |
| 76 | +хотя в~других языках может применяться какая-нибудь другая форма записи имени переменной |
| 77 | +(например, строка, состоящая из знака доллара и имени переменной); |
| 78 | +в~таком случае, конечно~же, ◊ic{symbol->variable} будет несколько сложнее. |
| 79 | + |
| 80 | +Если у~переменных на самом деле нет имён, то как~же ◊ic{lookup} будет их находить в~окружении? |
| 81 | +Вместо переменной как таковой следует использовать её программное представление, |
| 82 | +какой-нибудь уникальный ключ, по которому ◊ic{lookup} сможет отыскать переменную в~окружении. |
| 83 | +Так что ещё точнее было~бы записать: |
88 | 84 |
|
89 | 85 | ◊code:lisp{
|
90 | 86 | ... (lookup (variable->key (symbol->variable exp)) env) ...
|
91 | 87 | }
|
92 | 88 |
|
93 |
| -Однако, врождённая лень лисперов настаивает на использовании символов для |
94 |
| -ключей. Так что ◊ic{variable"~>key} — это лишь обратная функция |
95 |
| -к~◊ic{symbol◊discretionary{->}{}{->}variable}, и их последовательное применение |
96 |
| -никак не~изменяет~◊ic{exp}. |
97 |
| -
|
98 |
| -◊indexR{автоцитирование}◊indexR{механизм автоцитирования} |
99 |
| -Если выражение атомарное (то~есть не~является списком) и не~является символом, |
100 |
| -то соблазнительно его считать представлением какой-нибудь константы |
101 |
| -с~соответствующим значением. Такое поведение называется механизмом |
102 |
| -◊term{автоцитирования}. Автоцитируемый объект не~требует явного цитирования и |
103 |
| -имеет собственное значение. За примерами можно обратиться к~◊cite{cha96}. |
104 |
| -
|
105 |
| -Но является~ли такое поведение правильным? Во-первых, не~всегда атомарные |
106 |
| -объекты обозначают сами себя. Например, строка ◊ic{"a?b:c"} могла~бы означать |
107 |
| -вызов компилятора~Си, затем вызов получившейся программы и подстановку |
108 |
| -возвращаемого ей значения вместо этой строки. |
109 |
| -
|
110 |
| -С~другими объектами (вроде функций) вообще не~понятно, как именно их |
111 |
| -◊emph{вычислять}. К~примеру, ясно, что значением переменной ◊ic{car} является |
112 |
| -функция, возвращающая левый элемент пары, но что является значением самой |
113 |
| -функции ◊ii{car}? Чаще всего попытки вычисления значения функции считаются |
114 |
| -ошибочными. |
115 |
| -
|
116 |
| -◊indexC{()}◊indexR{пустой список, ◊protect◊ic{()}} |
117 |
| -Другой пример проблемного значения — пустой список~◊ic{()}. Судя по тому, что |
118 |
| -это список, он должен означать вызов функции. Вот только в~нём нет |
119 |
| -ни~аргументов, ни~самой функции. Такая запись в~Scheme запрещена и является |
120 |
| -синтаксической ошибкой. |
121 |
| -
|
122 |
| -Поэтому необходимо очень аккуратно анализировать программу и автоцитировать |
123 |
| -только те данные, для которых это явно стоить делать, например: числа, строки |
124 |
| -и~знаки. ◊seePage[basics/evaluating-forms/ssect:quoting] Так что мы записываем |
125 |
| -следующее: |
| 89 | +Однако, врождённая лень лисперов подсказывает, что в~качестве ключей можно использовать символы. |
| 90 | +Получается, что ◊ic{variable->key} — это лишь обратная функция к~◊ic{symbol->variable}, |
| 91 | +а~их последовательное применение никак не~изменяет~◊ic{exp}. |
| 92 | + |
| 93 | +◊indexR{автоцитирование} |
| 94 | +◊indexR{механизм автоцитирования} |
| 95 | +Если выражение атомарное (то~есть не~является списком) и~не~является символом, |
| 96 | +то соблазнительно его считать представлением константы с~соответствующим значением. |
| 97 | +Такое поведение называется механизмом ◊term{автоцитирования}. |
| 98 | +Автоцитируемый объект не~требует явного цитирования и имеет собственное значение. |
| 99 | +За~примерами можно обратиться к~◊cite{cha96}. |
| 100 | + |
| 101 | +Но~является~ли такое поведение правильным? |
| 102 | +Во-первых, атомарные объекты не~всегда обозначают сами себя. |
| 103 | +Например, строка ◊ic{"a?b:c"} могла~бы означать вызов компилятора~Си, |
| 104 | +затем исполнение получившейся программы и подстановку возвращаемого ей значения вместо этой~строки. |
| 105 | + |
| 106 | +С~другими объектами (вроде функций) вообще не~понятно, как именно их ◊emph{вычислять}. |
| 107 | +К~примеру, ясно, что значением переменной ◊ic{car} является функция, возвращающая левый элемент пары, |
| 108 | +но что является значением самой ◊emph{функции~◊ii{car}}? |
| 109 | +Чаще всего попытки вычисления значения функции считаются ошибочными. |
| 110 | + |
| 111 | +◊indexC{()} |
| 112 | +◊indexR{пустой список, ◊ic{()}} |
| 113 | +Другой пример проблемного значения — пустой список~◊ic{()}. |
| 114 | +Судя по тому, что это список, он должен означать вызов функции. |
| 115 | +Вот только в~нём нет ни~аргументов, ни~самой функции. |
| 116 | +Такая запись в~Scheme запрещена и считается синтаксической ошибкой. |
| 117 | + |
| 118 | +Поэтому необходимо очень аккуратно анализировать программу |
| 119 | +и автоцитировать только те данные, для которых это явно стоит делать, |
| 120 | +вроде чисел, строк, знаков. |
| 121 | +◊seePage{basics/evaluating-forms/ssect:quoting} |
| 122 | +Так что мы записываем следующее: |
126 | 123 |
|
127 | 124 | ◊code:lisp{
|
128 | 125 | (define (evaluate exp env)
|
|
138 | 135 | ◊indexR{ошибки!варианты обработки}
|
139 | 136 | ◊indexC{wrong}
|
140 | 137 | ◊phantomlabel{basic/atoms/para:the-first-error}
|
141 |
| -В~этом фрагменте кода виден первый случай, когда могут возникнуть ошибки. |
142 |
| -Большая часть лисп-систем имеет свои собственные механизмы обработки |
143 |
| -исключительных ситуаций, которые непросто сделать переносимыми. В~случае ошибки |
144 |
| -мы вызываем ◊ic{wrong}◊footnote{Заметьте, не~«функцию ◊ic{wrong}». Варианты её |
145 |
| -реализации подробнее рассматриваются в~разделе~◊ref{compilation/sect:exception}.} |
146 |
| -и передаём ей первым аргументом строку. В~строке находится текстовое описание |
147 |
| -ошибки, а следующие за ней аргументы несут дополнительную информацию о~том, что |
148 |
| -вызвало проблему. Системы с~зачаточным механизмом обработки ошибок обычно |
149 |
| -в~случае проблем выдают какие"~то непонятные надписи вроде ◊ic{"Bus error: core |
150 |
| -dumped"} и умирают. Другие останавливают текущие вычисления и возвращаются |
151 |
| -к~диалоговому режиму. А~третьи могут связывать с~вычисляемым выражением |
152 |
| -специальный обработчик исключений, который перехватит брошенный объект, |
153 |
| -описывающий ошибку, и будет уже по нему решать, что делать дальше. |
154 |
| -◊seePage[compilation/sect:exception] В~некоторых случаях даже реализуется |
155 |
| -подобие экспертной системы, которая анализирует ошибку, вызвавший её код и |
156 |
| -выдаёт пользователю варианты её исправления. В~общем, сложно сказать однозначно, |
157 |
| -что следует делать в~случае ошибки. |
| 138 | +В~исполняемой программе могут быть ошибки, |
| 139 | +которые интерпретатору надо как-то обрабатывать. |
| 140 | +Большинство лисп-систем имеет механизмы обработки исключительных ситуаций, |
| 141 | +но~у~каждой реализации они свои собственные и не~всегда переносимые между реализациями. |
| 142 | +В~случае ошибки мы будем вызывать ◊ic{wrong}, |
| 143 | +◊footnote{Заметьте, не~«◊emph{функцию}~◊ic{wrong}». |
| 144 | +Варианты её реализации подробнее рассматриваются в~разделе~◊ref{compilation/sect:exception}.} |
| 145 | +передавая первым аргументом строку. |
| 146 | +В~строке находится текстовое описание ошибки, |
| 147 | +а~следующие аргументы несут дополнительную информацию о~том, что вызвало проблему. |
| 148 | +Системы с~зачаточным механизмом обработки ошибок |
| 149 | +в~случае проблем обычно выдают какие-то непонятные надписи вроде ◊ic{"Bus~error: core~dumped"} и~умирают. |
| 150 | +Другие останавливают текущую работу и возвращаются к~диалоговому режиму. |
| 151 | +А~третьи могут связывать с~вычисляемым выражением специальный обработчик исключений, |
| 152 | +который перехватит объект, описывающий ошибку, и~будет уже по нему решать, что делать дальше. |
| 153 | +◊seePage{compilation/sect:exception} |
| 154 | +В~некоторых случаях даже реализуется подобие экспертной системы, |
| 155 | +которая самостоятельно анализирует ошибку, вызвавший её код, |
| 156 | +и выдаёт пользователю возможные варианты исправления возникшей ситуации. |
| 157 | +Короче говоря, сложно сказать однозначно, что следует делать в~случае~ошибки, |
| 158 | +поэтому пока~что мы будем просто вызывать~◊ic{wrong}. |
0 commit comments