Гомоиконность - Homoiconicity
В компьютерное программирование, гомоиконность (от Греческий слова гомо- означает "то же самое" и икона означает "представление") является свойством некоторых языки программирования. Язык гомоиконный если программа, написанная на нем, может управляться как данные с использованием языка, и, таким образом, внутреннее представление программы может быть выведено, просто читая саму программу. Например, Лисп Программа написана как обычный список Лиспа и может управляться другим кодом Лиспа.[1] Это свойство часто резюмируют, говоря, что язык рассматривает «код как данные».
В гомоиконическом языке первичным представлением программ также является структура данных в примитивный тип самого языка. Это делает метапрограммирование проще, чем на языке без этого свойства: отражение на языке (изучение сущностей программы на время выполнения ) зависит от единой однородной структуры, и ему не нужно обрабатывать несколько различных структур, которые могут появиться в сложном синтаксисе.
Как отмечалось выше, обычно цитируемым примером является Лисп, который был создан для упрощения манипуляций со списками и имеет структуру S-выражения которые принимают форму вложенный списки. Программы на Лиспе написаны в виде списков; в результате программа может получать доступ к своим собственным функциям и процедурам во время работы и программно изменять себя на лету. Гомиконические языки обычно включают полную поддержку синтаксические макросы, позволяя программисту кратко выражать преобразования программ. Примеры - языки программирования Clojure (современный диалект Лиспа), Ребол (также его преемник Красный ), Рефал, Пролог, и совсем недавно Юля[нужна цитата ].
История
Первоисточником является бумага Расширения макросов для языков компилятора,[2] согласно ранней и влиятельной газете TRAC, язык обработки текста:[3]
Одна из основных целей дизайна заключалась в том, чтобы сценарий ввода ПРОФ (то, что вводит пользователь) должно быть идентично тексту, который описывает внутренние действия процессора TRAC. Другими словами, процедуры TRAC должны храниться в памяти в виде строки символов в точности так, как пользователь вводил их с клавиатуры. Если процедуры TRAC сами развивают новые процедуры, эти новые процедуры также должны быть изложены в том же сценарии. Процессор TRAC в своем действии интерпретирует этот сценарий как свою программу. Другими словами, программа-переводчик TRAC (процессор) эффективно преобразует компьютер в новый компьютер с новым программным языком - языком TRAC. В любое время должна быть предусмотрена возможность отображения программной или процедурной информации в той же форме, в какой процессор TRAC будет воздействовать на нее во время ее выполнения. Желательно, чтобы внутреннее представление кода символа было идентично или очень похоже на представление внешнего кода. В настоящей реализации TRAC внутреннее символьное представление основано на ASCII. Поскольку процедуры и текст TRAC имеют одинаковое представление внутри и вне процессора, термин «гомоиконный» применим от «homo», означающего то же самое, и «пиктограммы», означающего представление.
[...]
По предложению Маккалоу, У.С., на основе терминологии Пирса, С.С. Макилроя. M.D., "Расширения макросов для языков компиляторов", Comm. ACM, стр. 214–220; Апрель 1960 г.
Алан Кей использовал и, возможно, популяризировал термин «гомоиконический», используя его в своей докторской диссертации 1969 года:[4]
Заметной группой исключений из всех предыдущих систем являются Интерактивный LISP [...] и TRAC. Оба функционально ориентированы (один список, другая строка), оба общаются с пользователем на одном языке, и оба являются «гомоиконными» в том смысле, что их внутреннее и внешнее представления по существу одинаковы. Оба они обладают способностью динамически создавать новые функции, которые затем могут быть доработаны по желанию пользователя. Единственный их большой недостаток - написанные на них программы выглядят как King Бурнибуриах Письмо к шумеру сделано вавилонской клиниформой! [...]
Использование и преимущества
Одним из преимуществ гомоиконности является то, что расширение языка новыми концепциями обычно становится проще, поскольку данные, представляющие код, могут передаваться между мета и базовый уровень программы. В абстрактное синтаксическое дерево функции могут быть составлены и обработаны как структура данных на метаслое, а затем оценен. Может быть намного легче понять, как управлять кодом, поскольку его легче понять как простые данные (поскольку формат самого языка является форматом данных).
Типичным проявлением гомиконности является мета-круговой оценщик.
Методы реализации
Все Архитектура фон Неймана системы, к которым сегодня относится подавляющее большинство компьютеров общего назначения, могут быть неявно описаны как гомоиконные из-за способа, которым необработанный машинный код выполняется в памяти, причем тип данных - байты в памяти. Однако эту функцию можно также абстрагировать до уровня языка программирования.
Такие языки как Лисп и его диалекты,[5] такие как Схема,[6] Clojure[1], Ракетка[2] нанять S-выражения для достижения гомиконности.
Другие языки, которые считаются гомиконическими, включают:
В Лиспе
Лисп использует S-выражения как внешнее представление данных и кода. S-выражения можно читать с помощью примитивной функции Lisp ЧИТАТЬ
. ЧИТАТЬ
возвращает данные Лиспа: списки, символы, числа, строки. Примитивная функция Lisp EVAL
использует код Лиспа, представленный как данные Лиспа, вычисляет побочные эффекты и возвращает результат. Результат будет напечатан примитивной функцией РАСПЕЧАТАТЬ
, который создает внешнее S-выражение из данных Лиспа.
Данные Лиспа, список с использованием различных типов данных: (под) списки, символы, строки и целые числа.
((:имя "Джон" :возраст 20) (:имя "Мэри" :возраст 18) (:имя "алиса" :возраст 22))
Код на Лиспе. В примере используются списки, символы и числа.
(* (грех 1.1) (потому что 2.03)) ; инфиксно: sin (1.1) * cos (2.03)
Создайте указанное выше выражение с помощью примитивной функции Lisp СПИСОК
и установите переменную ВЫРАЖЕНИЕ
к результату
(setf выражение (список '* (список грех 1.1) (список 'потому что 2.03)) ) -> (* (ГРЕХ 1.1) (COS 2.03)) ; Lisp возвращает и печатает результат(в третьих выражение) ; третий элемент выражения-> (COS 2.03)
Изменить COS
срок до ГРЕХ
(setf (первый (в третьих выражение)) ГРЕХ); Выражение теперь (* (SIN 1.1) (SIN 2.03)).
Оцените выражение
(оценка выражение)-> 0.7988834
Вывести выражение в строку
(печать в строку выражение)-> "(* (SIN 1.1) (SIN 2.03))"
Прочитать выражение из строки
(чтение из строки "(* (SIN 1.1) (SIN 2.03))")-> (* (ГРЕХ 1.1) (ГРЕХ 2.03)) ; возвращает список списков, чисел и символов
В Прологе
1 ?- Икс является 2*5.Икс = 10.2 ?- L = (Икс является 2*5), write_canonical(L).является(_, *(2, 5))L = (Икс является 2*5).3 ?- L = (десять(Икс):-(Икс является 2*5)), write_canonical(L).:-(десять(А), является(А, *(2, 5)))L = (десять(Икс):-ИКС является 2*5).4 ?- L = (десять(Икс):-(Икс является 2*5)), утверждать(L).L = (десять(Икс):-ИКС является 2*5).5 ?- десять(Икс).Икс = 10.6 ?-
В строке 4 мы создаем новое предложение. Оператор :-
разделяет главу и тело предложения. С участием assert / 1 *
мы добавляем его в существующие пункты (добавляем в «базу данных»), чтобы мы могли вызвать его позже. На других языках мы бы назвали это «созданием функции во время выполнения». Мы также можем удалить предложения из базы данных с помощью отменить / 1
, или убрать / 1
.
* Число после имени предложения - это количество аргументов, которые оно может принимать. Его еще называют арность.
Мы также можем запросить базу данных, чтобы получить тело предложения:
7 ?- пункт(десять(Икс),Y).Y = (Икс является 2*5).8 ?- пункт(десять(Икс),Y), Y = (Икс является Z).Y = (Икс является 2*5),Z = 2*5.9 ?- пункт(десять(Икс),Y), вызов(Y).Икс = 10,Y = (10 является 2*5).
вызов
аналогичен Lisp оценка
функция.
В Реболе
Концепция обработки кода как данных, а также манипулирование ими и их оценка могут быть очень четко продемонстрированы в Ребол. (Rebol, в отличие от Lisp, не требует скобок для разделения выражений).
Ниже приведен пример кода в Rebol (обратите внимание, что >>
представляет подсказку интерпретатора; для удобства чтения добавлены пробелы между некоторыми элементами):
>> повторение я 3 [ Распечатать [ я "Здравствуйте" ] ]
1 привет2 привет3 привет
(повторение
фактически встроенная функция в Rebol, а не языковая конструкция или ключевое слово).
Заключая код в квадратные скобки, интерпретатор не оценивает его, а просто рассматривает его как блок, содержащий слова:
[ повторение я 3 [ Распечатать [ я "Здравствуйте" ] ] ]
Этот блок имеет типовой блок! и, кроме того, может быть назначен как значение слова с использованием того, что кажется синтаксисом для присвоения, но фактически понимается интерпретатором как специальный тип (установочное слово!
) и принимает форму слова, за которым следует двоеточие:
>>блокировать1: [ повторение я 3 [ Распечатать [ я "Здравствуйте" ] ] ]
;; Присвойте значение блока слову `block1` == [repeat i 3 [print [i" hello "]]] >>тип? блок1
;; Оцените тип слова `block1` == block!
Блок все еще можно интерпретировать, используя делать
функция, предоставляемая в Rebol (аналогична оценка
в Лиспе ).
Можно опросить элементы блока и изменить их значения, тем самым изменив поведение кода, если он должен быть оценен:
>>блок1/3
;; Третий элемент блока == 3 >>блок1/3: 5
;; Установите значение 3-го элемента на 5 == 5 >>зонд блок1
;; Показать измененный блок == [повторить i 5 [print [i "hello"]]] >>делать блок1
;; Оцените блок1 hello2 hello3 hello4 hello5 hello
Смотрите также
- Когнитивные измерения обозначений, принципы построения синтаксиса языков программирования
- Конкатенативный язык программирования
- Языко-ориентированное программирование
- Символическое программирование
- Самомодифицирующийся код
- LISP (язык программирования), пожалуй, самый известный пример гомиконического языка
- Метапрограммирование, метод программирования, для которого очень полезна гомиконность
- Реификация (информатика)
использованная литература
- ^ Уилер, Дэвид А. "Читаемые S-выражения на Лиспе".
- ^ Макилрой, Дуглас (1960). "Расширения макросов для языков компилятора". Comm. ACM. 3 (4): 214–220. Дои:10.1145/367177.367223.
- ^ Муерс, К.; Дойч, Л. (1965). "TRAC, язык обработки текста". Proceeding ACM '65 Proceedings 20-й национальной конференции 1965 г.. С. 229–246. Дои:10.1145/800197.806048.
- ^ Кей, Алан (1969). Реактивный двигатель (Кандидат наук). Университет Юты.
- ^ а б c d е ж г час я Гомиконические языки
- ^ а б Гомоиконические языки (в архиве), в Настоящий синий блог в Oracle
- ^ «Почему мы создали Юлию». julialang.org.
Нам нужен язык, который является гомоиконным, с настоящими макросами, такими как Lisp, но с очевидными, знакомыми математическими обозначениями, такими как Matlab.
- ^ «метапрограммирование». docs.julialang.org.
Как и Lisp, Julia представляет собственный код как структуру данных самого языка.
- ^ «Метапрограммирование в математике». Обмен стеком.
Mathematica - это [...] гомоиконический язык (программы, написанные на собственных структурах данных - выражениях Mathematica. Это парадигма кода как данных, такая как Lisp, который использует для этого списки)
- ^ Shapiro, Ehud Y .; Стерлинг, Леон (1994). Искусство Пролога: передовые методы программирования. MIT Press. ISBN 0-262-19338-8.
- ^ Ramsay, S .; Пытлик-Циллиг, Б. (2012). «Методы создания кода для взаимодействия коллекций XML». dh2012 Материалы конференции по цифровым гуманитарным наукам.
- ^ «Заметки для экспертов по языку программирования». Язык Wolfram Language. Вольфрам. 2017 г.