В компьютерном программировании , homoiconicity (от греческих слов гомо- означает «то же» и значок означает «представление») является свойством некоторых языков программирования . Язык является гомоиконным, если программа, написанная на нем, может обрабатываться как данные с использованием языка, и, таким образом, внутреннее представление программы может быть выведено, просто читая саму программу. Это свойство часто резюмируют, говоря, что язык рассматривает «код как данные».
В гомоиконическом языке первичным представлением программ также является структура данных в примитивном типе самого языка. Это делает метапрограммирование проще, чем на языке без этого свойства: отражение на языке (изучение сущностей программы во время выполнения ) зависит от единой однородной структуры, и ему не нужно обрабатывать несколько различных структур, которые могли бы появиться в сложном синтаксисе. Гомиконические языки обычно включают полную поддержку синтаксических макросов , что позволяет программисту кратко выражать преобразования программ.
Часто цитируемым примером является Лисп , который был создан для упрощения манипуляций со списками и где структура задается S-выражениями, которые принимают форму вложенных списков, и может управляться другим кодом Лиспа. [1] Другими примерами являются языки программирования Clojure (современный диалект Lisp), Rebol (также его преемник Red ), Refal , Prolog и, совсем недавно, Julia .
История
Первоисточником является документ Macro Instruction Extensions of Compiler Languages , [2] согласно раннему и влиятельному документу TRAC, A Text-Handling Language : [3]
Одна из основных целей разработки заключалась в том, чтобы сценарий ввода TRAC (то, что вводит пользователь) был идентичен тексту, который руководит внутренними действиями процессора TRAC. Другими словами, процедуры TRAC должны храниться в памяти в виде строки символов в точности так, как пользователь вводил их с клавиатуры. Если процедуры TRAC сами развивают новые процедуры, эти новые процедуры также должны быть изложены в том же сценарии. Процессор TRAC в своем действии интерпретирует этот сценарий как свою программу. Другими словами, программа-переводчик TRAC (процессор) эффективно преобразует компьютер в новый компьютер с новым программным языком - языком TRAC. В любое время должна быть возможность отображать программную или процедурную информацию в той же форме, в какой процессор TRAC будет воздействовать на нее во время ее выполнения. Желательно, чтобы внутреннее представление кода символа было идентично или очень похоже на представление внешнего кода. В существующей реализации TRAC внутреннее символьное представление основано на ASCII . Поскольку процедуры и текст TRAC имеют одинаковое представление внутри и вне процессора, термин «гомоиконный» применим от слова «homo», означающего одно и то же, и значка, означающего «представление».
[...]
Следуя предложению Маккалоу, WS , основанному на терминологии Пирса, Макилроя из CS. MD, "Расширения макросов для языков компилятора", Comm. ACM, стр. 214–220; Апрель 1960 г.
Алан Кей использовал и, возможно, популяризировал термин «гомоиконический», используя его в своей докторской диссертации 1969 года: [4]
Заметной группой исключений из всех предыдущих систем являются Интерактивный LISP [...] и TRAC. Оба функционально ориентированы (один список, другая строка), оба разговаривают с пользователем на одном языке, и оба являются «гомоиконными» в том смысле, что их внутреннее и внешнее представления по существу одинаковы. У них обоих есть возможность динамически создавать новые функции, которые затем могут быть доработаны по желанию пользователя. Их единственный большой недостаток в том, что написанные на них программы выглядят как письмо царя Бурнибуриаха к шумеру, написанное вавилонской клиникой! [...]
Использование и преимущества
Одним из преимуществ гомоиконности является то, что расширение языка новыми концепциями обычно становится проще, поскольку данные, представляющие код, могут передаваться между мета- уровнем и базовым уровнем программы. Абстрактное синтаксическое дерево функции может быть составлено и манипулировать ими в виде структуры данных в мета - слое, а затем оценивал . Может быть намного проще понять, как манипулировать кодом, поскольку его легче понять как простые данные (поскольку формат самого языка является форматом данных).
Типичной демонстрацией гомико-тонности является метакруговой оценщик .
Методы реализации
Все архитектурные системы фон Неймана , к которым сегодня относится подавляющее большинство компьютеров общего назначения, могут быть неявно описаны как гомоиконные из-за того, как необработанный машинный код выполняется в памяти, причем тип данных - байты в памяти. Однако эту функцию также можно абстрагировать до уровня языка программирования.
Такие языки, как Lisp и его диалекты [5], такие как Scheme , [6] Clojure и Racket, используют S-выражения для достижения гомоиконности.
Другие языки, которые считаются гомиконическими, включают:
В Лиспе
Lisp использует S-выражения как внешнее представление данных и кода. S-выражения можно читать с помощью примитивной функции Lisp READ
. READ
возвращает данные Лиспа: списки, символы , числа, строки. Примитивная функция Lisp EVAL
использует код Lisp, представленный как данные Lisp, вычисляет побочные эффекты и возвращает результат. Результат будет напечатан примитивной функцией PRINT
, которая создает внешнее S-выражение из данных Лиспа.
Данные Лиспа, список с использованием различных типов данных: (под) списки, символы, строки и целые числа.
(( : name "john" : возраст 20 ) ( : name "mary" : возраст 18 ) ( : name "alice" : возраст 22 ))
Код на Лиспе. В примере используются списки, символы и числа.
( * ( sin 1.1 ) ( cos 2.03 )) ; инфиксно: sin (1.1) * cos (2.03)
Создайте указанное выше выражение с помощью примитивной функции Lisp LIST
и установите переменную EXPRESSION
в результат
( выражение setf ( список '* ( список ' sin 1.1 ) ( список 'cos 2.03 )) ) -> ( * ( SIN 1.1 ) ( COS 2.03 )) ; Lisp возвращает и печатает результат ( третье выражение ) ; третий элемент выражения -> ( COS 2.03 )
Измените COS
термин наSIN
( setf ( первое ( третье выражение )) 'SIN ) ; Выражение теперь (* (SIN 1.1) (SIN 2.03)).
Оцените выражение
( выражение eval ) -> 0,7988834
Вывести выражение в строку
( выражение для печати в строку ) -> «(* (SIN 1.1) (SIN 2.03))»
Прочитать выражение из строки
( чтение из строки "(* (SIN 1.1) (SIN 2.03))" ) -> ( * ( SIN 1.1 ) ( SIN 2.03 )) ; возвращает список списков, чисел и символов
В Прологе
1 ? - X равно 2 * 5. Х = 10.2 ? - L = ( X равно 2 * 5 ) , write_canonical ( L ) . равно ( _, * ( 2 , 5 )) L = ( X равно 2 * 5 ) .3 ? - L = ( десять ( X ) : - ( X равно 2 * 5 )) , write_canonical ( L ) . : - ( ten ( A ) , is ( A, * ( 2 , 5 ))) L = ( ten ( X ) : -X is 2 * 5 ) .4 ? - L = ( десять ( X ) : - ( X равно 2 * 5 )) , assert ( L ) . L = ( десять ( X ) : -X равно 2 * 5 ) .5 ? - десять ( X ) . Х = 10.6 ? -
В строке 4 мы создаем новое предложение. Оператор :-
разделяет заголовок и тело предложения. С помощью assert/1*
мы добавляем его к существующим предложениям (добавляем его в «базу данных»), чтобы мы могли вызвать его позже. На других языках мы бы назвали это «созданием функции во время выполнения». Мы также можем удалить предложения из базы данных с помощью abolish/1
, или retract/1
.
* Число после имени предложения - это количество аргументов, которые оно может принимать. Это еще называют арностью .
Мы также можем запросить базу данных, чтобы получить тело предложения:
7 ? - пункт ( десять ( X ) , Y ) . Y = ( X равно 2 * 5 ) .8 ? - пункт ( десять ( X ) , Y ) , Y = ( X is Z ) . Y = ( X равно 2 * 5 ) , Z = 2 * 5.9 ? - пункт ( десятка ( X ) , Y ) , звонок ( Y ) . X = 10 , Y = ( 10 равно 2 * 5 ) .
call
аналогична eval
функции Лиспа .
В Реболе
Концепция обработки кода как данных, а также манипуляции с ними и их оценка могут быть очень четко продемонстрированы в Rebol . (Rebol, в отличие от Lisp, не требует скобок для разделения выражений).
Ниже приведен пример кода в Rebol (обратите внимание, что >>
это подсказка интерпретатора; для удобства чтения были добавлены пробелы между некоторыми элементами):
>> repeat i 3 [ print [ i "hello" ] ]
1 привет2 привет3 привет
( repeat
на самом деле это встроенная функция в Rebol, а не языковая конструкция или ключевое слово).
Заключая код в квадратные скобки, интерпретатор не оценивает его, а просто рассматривает его как блок, содержащий слова:
[ повторить i 3 [ print [ i "привет" ]]]
Этот блок имеет типовой блок! и, кроме того, может быть назначен как значение слова с использованием того, что кажется синтаксисом для присваивания, но фактически понимается интерпретатором как специальный тип ( set-word!
) и принимает форму слова, за которым следует двоеточие:
>> ;; Присвойте значение блока слову `block1`block1: [ repeat i 3 [ print [ i "hello" ] ] ]
== [повторить 3 [напечатать [я "привет"]]]>> ;; Оцените тип слова `block1`type? block1
== блок!
Блок по-прежнему можно интерпретировать с помощью do
функции, предоставленной в Rebol (аналогично evalLisp ).
Можно опросить элементы блока и изменить их значения, тем самым изменив поведение кода, если он должен быть оценен:
>> ;; Третий элемент блокаblock1/3
== 3>> ;; Установите значение 3-го элемента на 5block1/3: 5
== 5>> ;; Показать измененный блокprobe block1
== [повторить 5 [печать [я "привет"]]]>> ;; Оцените блокdo block1
1 привет2 привет3 привет4 привет5 привет
Смотрите также
- Когнитивные измерения нотаций , принципы построения синтаксиса языков программирования
- Конкатенативный язык программирования
- Языко-ориентированное программирование
- Символическое программирование
- Самомодифицирующийся код
- LISP (язык программирования) , пожалуй, самый известный пример гомоиконного языка
- Метапрограммирование , метод программирования, для которого очень полезен гомоиконность.
- Реификация (информатика)
Рекомендации
- ^ Уиллер, Дэвид А. "Читаемые S-выражения Лиспа" .
- ^ Макилрой, Дуглас (1960). «Расширения макросов для языков компилятора». Comm. ACM . 3 (4): 214–220. DOI : 10.1145 / 367177.367223 .
- ^ Муерс, CN ; Deutsch, LP (1965). "TRAC, язык обработки текста". Материалы ACM '65 Материалы 20-й национальной конференции 1965 года . С. 229–246. DOI : 10,1145 / 800197.806048 .
- ^ Кей, Алан (1969). Реактивный двигатель (PhD). Университет Юты.
- ^ a b c d e f g h i Гомиконические языки
- ^ a b Гомоиконические языки (из архива) , в блоге True Blue в Oracle
- ^ «Лиспи Эликсир» . 8thlight.com .
На первый взгляд, эликсир не гомиконичен. Однако синтаксис на поверхности - это всего лишь фасад для гомоиконической структуры под ним.
- ^ «Зачем мы создали Юлию» . julialang.org .
Нам нужен язык, который является гомоиконным, с настоящими макросами, такими как Lisp, но с очевидными, знакомыми математическими обозначениями, такими как Matlab.
- ^ «метапрограммирование» . docs.julialang.org .
Как и Лисп, Джулия представляет свой собственный код как структуру данных самого языка.
- ^ «Метапрограммирование в математике» . Обмен стеками .
Mathematica - это [...] гомоиконический язык (программы, написанные на собственных структурах данных - выражениях Mathematica. Это парадигма кода как данных, такая же, как Lisp, который использует для этого списки)
- ^ Shapiro, Ehud Y .; Стерлинг, Леон (1994). Искусство Пролога: передовые методы программирования . MIT Press. ISBN 0-262-19338-8.
- ^ Ramsay, S .; Пытлик-Зиллиг, Б. (2012). «Методы создания кода для взаимодействия коллекций XML» . dh2012 Материалы конференции по цифровым гуманитарным наукам .
- ^ «Заметки для знатоков языков программирования» . Язык Wolfram Language . Вольфрам. 2017 г.
Внешние ссылки
- Определение Homoiconic в C2 Wiki