Гомоиконность - 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

Смотрите также

использованная литература

  1. ^ Уилер, Дэвид А. "Читаемые S-выражения на Лиспе".
  2. ^ Макилрой, Дуглас (1960). "Расширения макросов для языков компилятора". Comm. ACM. 3 (4): 214–220. Дои:10.1145/367177.367223.
  3. ^ Муерс, К.; Дойч, Л. (1965). "TRAC, язык обработки текста". Proceeding ACM '65 Proceedings 20-й национальной конференции 1965 г.. С. 229–246. Дои:10.1145/800197.806048.
  4. ^ Кей, Алан (1969). Реактивный двигатель (Кандидат наук). Университет Юты.
  5. ^ а б c d е ж г час я Гомиконические языки
  6. ^ а б Гомоиконические языки (в архиве), в Настоящий синий блог в Oracle
  7. ^ «Почему мы создали Юлию». julialang.org. Нам нужен язык, который является гомоиконным, с настоящими макросами, такими как Lisp, но с очевидными, знакомыми математическими обозначениями, такими как Matlab.
  8. ^ «метапрограммирование». docs.julialang.org. Как и Lisp, Julia представляет собственный код как структуру данных самого языка.
  9. ^ «Метапрограммирование в математике». Обмен стеком. Mathematica - это [...] гомоиконический язык (программы, написанные на собственных структурах данных - выражениях Mathematica. Это парадигма кода как данных, такая как Lisp, который использует для этого списки)
  10. ^ Shapiro, Ehud Y .; Стерлинг, Леон (1994). Искусство Пролога: передовые методы программирования. MIT Press. ISBN  0-262-19338-8.
  11. ^ Ramsay, S .; Пытлик-Циллиг, Б. (2012). «Методы создания кода для взаимодействия коллекций XML». dh2012 Материалы конференции по цифровым гуманитарным наукам.
  12. ^ «Заметки для экспертов по языку программирования». Язык Wolfram Language. Вольфрам. 2017 г.

внешние ссылки