Что именно является символом в lisp/ схеме?
Ради любви всемогущего, мне еще предстоит понять цель символа 'iamasymbol
, Я понимаю числа, логические значения, строки... переменные. Но символы - это слишком много для моего маленького ума, требующего императивного мышления. Для чего именно я их использую? Как они должны использоваться в программе? Мое понимание этой концепции просто неудачно.
6 ответов
В схеме и ракетке символ похож на неизменную строку, которая оказывается интернированной, так что символы можно сравнивать с eq?
(быстрое, по сути, сравнение указателей). Символы и строки являются отдельными типами данных.
Одно использование для символов - легкие перечисления. Например, можно сказать, что направление 'north
, 'south
, 'east
, или же 'west
, Конечно, вы можете использовать строки для той же цели, но это будет немного менее эффективно. Использование чисел было бы плохой идеей; представлять информацию в максимально понятной и прозрачной форме.
В другом примере SXML - это представление XML с использованием списков, символов и строк. В частности, строки представляют символьные данные, а символы представляют имена элементов. Таким образом, XML <em>hello world</em>
будет представлен значением (list 'em "hello world")
, который можно записать более компактно '(em "hello world")
,
Другое использование для символов в качестве ключей. Например, вы можете реализовать таблицу методов в виде словаря, отображающего символы в функции реализации. Чтобы вызвать метод, вы ищите символ, который соответствует имени метода. Lisp/Scheme/Racket делает это действительно простым, потому что язык уже имеет встроенное соответствие между идентификаторами (часть синтаксиса языка) и символами (значениями в языке). Это соответствие позволяет легко поддерживать макросы, которые реализуют пользовательские синтаксические расширения языка. Например, можно реализовать систему классов как макробиблиотеку, используя неявное соответствие между "именами методов" (синтаксическое понятие, определенное системой классов) и символами:
(send obj meth arg1 arg2)
=>
(apply (lookup-method obj 'meth) obj (list arg1 arg2))
(В других Лиспах то, что я сказал, в основном верно, но есть еще кое-что, о чем нужно знать, например, пакеты и функция против переменных слотов, IIRC.)
Символ - это объект с простым строковым представлением, которое (по умолчанию) гарантированно будет интернировано; т. е. любые два символа, которые написаны одинаково, являются одним и тем же объектом в памяти (ссылочное равенство).
Почему в Лиспе есть символы? Ну, это в значительной степени является артефактом того факта, что Лиспс встраивает свой собственный синтаксис в качестве типа данных языка. Компиляторы и интерпретаторы используют символы для представления идентификаторов в программе; Поскольку Lisp позволяет вам представлять синтаксис программы в виде данных, он предоставляет символы, потому что они являются частью представления.
Чем они полезны помимо этого? Ну и несколько вещей:
- Лисп обычно используется для реализации встроенных доменных языков. Многие из методов, используемых для этого, происходят из мира компиляторов, поэтому символы являются здесь полезным инструментом.
- Макросы в Common Lisp обычно включают в себя работу с символами более подробно, чем этот ответ. (Хотя, в частности, генерация уникальных идентификаторов для расширений макросов требует возможности генерировать символ, который гарантированно никогда не будет равен любому другому.)
- Фиксированные типы перечисления лучше реализованы как символы, чем строки, потому что символы можно сравнивать по ссылочному равенству.
- Существует много структур данных, которые вы можете построить, где вы можете получить выигрыш в производительности от использования символов и равенства ссылок.
Символы в lisp являются удобочитаемыми идентификаторами. Они все одинокие. Так что если вы объявите 'foo где-нибудь в своем коде, а затем снова используете' foo, это будет указывать на то же место в памяти.
Пример использования: разные символы могут представлять разные фигуры на шахматной доске.
Из структуры и интерпретации компьютерных программ, второе издание Гарольда Абельсона и Джеральда Джея Суссмана, 1996:
Чтобы манипулировать символами, нам нужен новый элемент в нашем языке: возможность заключать в кавычки объект данных. Предположим, мы хотим построить список (a b). Мы не можем сделать это с помощью (list a b), потому что это выражение создает список значений a и b, а не сами символы. Эта проблема хорошо известна в контексте естественных языков, где слова и предложения могут рассматриваться как семантические объекты или как символьные строки (синтаксические объекты). Обычная практика в естественных языках - использовать кавычки, чтобы указать, что слово или предложение следует трактовать буквально как строку символов. Например, первая буква "Джон" явно "J." Если мы скажем кому-то "произнесите ваше имя вслух", мы ожидаем услышать имя этого человека. Однако, если мы скажем кому-то "произнести" ваше имя "вслух", мы ожидаем услышать слова "ваше имя". Обратите внимание, что мы вынуждены вставлять кавычки, чтобы описать то, что может сказать кто-то другой. Мы можем следовать этой же практике, чтобы идентифицировать списки и символы, которые должны рассматриваться как объекты данных, а не как выражения для оценки. Однако наш формат цитирования отличается от формата естественных языков тем, что мы ставим кавычку (традиционно символ одинарной кавычки) только в начале цитируемого объекта. Мы можем избежать этого в синтаксисе Схемы, потому что мы используем пробелы и круглые скобки для разделения объектов. Таким образом, значение символа одинарной кавычки заключается в цитировании следующего объекта. Теперь мы можем различать символы и их значения:
(define a 1)
(define b 2)
(list a b)
(1 2)
(list ’a ’b)
(a b)
(list ’a b)
(a 2)
Списки, содержащие символы, могут выглядеть как выражения нашего языка:
(* (+ 23 45) (+ x 9))
(define (fact n) (if (= n 1) 1 (* n (fact (- n 1)))))
Пример: символическое дифференцирование
Символ - это просто специальное имя для значения. Значением может быть что угодно, но символ используется для ссылки на одно и то же значение каждый раз, и такого рода вещи используются для быстрых сравнений. Поскольку вы говорите, что вы - императивное мышление, они похожи на числовые константы в C, и именно так они обычно реализуются (внутренне хранимые числа).
Чтобы проиллюстрировать точку зрения Луиса Касильяса, может быть полезно наблюдать, чем символы отличаются от строк.
Пример ниже относится к mit-схеме (версия 10.1.10). Для удобства я использую эту функцию как
eval
:
(define my-eval (lambda (x) (eval x (scheme-report-environment 5))))
Символ может легко вычисляться как значение или функция, которую он называет:
(define s 2) ;Value: s
(my-eval "s") ;Value: "s"
(my-eval s) ;Value: 2
(define a '+) ;Value: a
(define b "+") ;Value: b
(my-eval a) ;Value: #[arity-dispatched-procedure 12]
(my-eval b) ;Value: "+"
((my-eval a) 2 3) ;Value: 5
((my-eval b) 2 3) ;ERROR: The object "+" is not applicable.