Подтвердить что ты не робот

Почему кодовые данные?

Что такое код-данные? Я слышал, что он превосходит "code-as-ascii-characters", но почему? Я лично считаю философию кода как данных немного запутанной.

Я пробовал на Схеме, но я никогда не получал всю информацию о кодах-данных и не задавался вопросом, что именно это означает?

4b9b3361

Ответ 1

Это означает, что ваш программный код, который вы пишете, также является данными, которыми может управлять программа. Возьмите простое выражение схемы, например

(+ 3 (* 6 7))

Вы можете рассматривать это как математическое выражение, которое при оценке дает значение. Но это также список, содержащий три элемента, а именно +, 3 и (* 6 7). Процитировав список,

 '(+ 3 (* 6 7))

Вы говорите схеме, чтобы рассматривать ее как последнюю, а именно список, содержащий три элемента. Таким образом, вы можете управлять этим списком с помощью программы, а затем оценивать ее. Сила, которую она дает вам, огромна, и когда вы "получаете" эту идею, есть несколько очень интересных трюков, которые нужно сыграть.

Ответ 2

Кодовые данные на самом деле являются только одной стороной монеты. Другой - data-as-code.

Возможность вставлять произвольные данные в код Lisp и загружать и перезагружать его "на лету" делает его (данные) очень удобным для обработки, потому что он может устранить любое несоответствие потенциального импеданса между образом представления данных и способом код работает.

Позвольте мне привести пример.

Скажем, вы хотите написать какую-то компьютерную игру с различными классами монстров. У вас есть в основном два варианта: моделировать классы монстров в вашем языке программирования или использовать подход, основанный на данных, где описания классов считываются, скажем, из XML файла.

Выполнение моделирования на языке программирования имеет преимущества простоты использования и простоты (что всегда хорошо). Также легко указать индивидуальное поведение в зависимости от класса монстра по мере необходимости. Наконец, реализация, вероятно, довольно оптимизирована.

С другой стороны, загрузка всего из файлов данных намного более гибкая. Вы можете выполнять множественное наследование, если язык не поддерживает его; вы можете выполнять динамическую типизацию; вы можете загружать и перезагружать вещи во время выполнения; вы можете использовать простой, точечный, специфичный для домена синтаксис и многое другое. Но теперь вам нужно написать какую-то среду выполнения во всем этом, а определение поведения означает либо разделение данных между файлами данных и кодом игры, либо встраивание языка сценариев, что является еще одним слоем случайной сложности.

Или вы можете сделать это способом Lisp: укажите свой собственный подъязык, переведите его в код и выполните его. Если язык программирования, который вы используете, достаточно динамичен и синтаксически гибкий, вы получаете все преимущества от использования подхода, основанного на данных (поскольку код - это данные) в сочетании с простотой хранения всего в коде (поскольку данные являются кодом).

Это не относится к Lisp, кстати. Существуют различные оттенки эквивалентности кодовых данных, серые между Lisp и, скажем, С++. Например, Ruby делает внедрение данных в приложение проще, чем Python, и Python упрощает работу над Java. Оба типа данных как кода и кода как данные являются скорее континуумом, чем они являются - или вопросами.

Ответ 3

Как программист Lisp, вы научитесь думать о источнике программы как о данных. Это уже не статический текст, а данные. В некоторых формах Lisp сама программа представляет собой структуру данных, которая выполняется.

Затем все инструменты ориентированы таким образом. Вместо текстового макропроцессора Lisp имеется макросистема, которая работает над программами как данные. Преобразование программ в текст и из него также имеет свои инструменты.

Подумайте о добавлении двух элементов вектора:

(let ((v (vector 1 2 3)))
   (+ (aref v 0)
      (aref v 1)))

В этом нет ничего необычного. Вы можете скомпилировать и запустить его.

Но вы также можете это сделать:

(let ((v (vector 1 2 3)))
   (list '+
         (list 'aref v 0)
         (list 'aref v 1)))

Это возвращает список с символом плюс и двумя подсписками. Эти подсписки имеют символ aref, затем значение массива v и значение индекса.

Это означает, что построенная программа содержит фактически символы, а также данные. Массив действительно является частью подсписок. Таким образом, вы можете создавать программы, и эти программы являются данными и могут содержать произвольные данные.

Затем EVAL оценивает программу как данные.

CL-USER 17 > (setf *print-circle* t)
=>  T

Выше сказано, что принтер должен печатать круговые структуры данных таким образом, чтобы идентификаторы сохранялись при чтении.

CL-USER 18 > (let ((v (vector 1 2 3)))
               (list '+
                     (list 'aref v 0)
                     (list 'aref v 1)))
=>  (+ (AREF #1=#(1 2 3) 0) (AREF #1# 1))

Теперь пусть eval данные как программа Lisp:

CL-USER 19 > (EVAL (let ((v (vector 1 2 3)))
                     (list '+
                           (list 'aref v 0)
                           (list 'aref v 1))))

=>  3

Если ваш компилятор ожидает, что текст как источник, вы можете построить эти тексты, но они никогда не смогут напрямую ссылаться на данные. Для этой текстовой исходной конструкции было разработано много инструментов, но многие из них, как правило, работают поэтапно. В Lisp функциональность манипулирования данными может быть непосредственно применена для управления программами, и эта функциональность непосредственно встроена и часть процесса оценки.

Итак, Lisp дает вам дополнительную степень свободы и новые способы мышления.

Ответ 4

Код-как-данные ссылаются на то, что ваш код выражается в терминах структур данных языка. Я бы не стал спорить здесь, что это лучший способ программирования, но я считаю, что это прекрасный способ выразить идеи в коде.

Одним из преимуществ является то, что метапрограммирование практически не отличается от обычного программирования. С символами code-as-ascii вам часто приходится выполнять серьезный синтаксический анализ, чтобы сделать что-либо мета, и вы пропускаете эти неприятные биты с помощью Lisp.

Ответ 5

В схеме (или любом Lisp) вы можете объявить литералы списка следующим образом:

> '(1 2 3)
=> (1 2 3)

Это похоже на многие другие языки высокого уровня, за исключением незначительных различий в обозначениях. Например, так некоторые другие языки представляют список литералов:

[1, 2, 3] # Python
#(1 2 3) "Smalltalk. This is in fact an array in Smalltalk. Let us ignore that for now."

Списки могут содержать любые значения. Поскольку функции являются первоклассными объектами, список также может содержать функции. Заменим первый элемент в приведенном выше списке на функцию:

> '(+ 2 3)
=> (+ 2 3)

Одинарная кавычка (') идентифицирует литерал списка. (Также как # в Smalltalk). Что произойдет, если мы удалим цитату? Тогда интерпретатор Схемы будет обрабатывать список специально. Он рассмотрит первый элемент как функцию (или процедуру), а остальные элементы - как аргументы этой функции. Функция выполняется (или оценивается):

> (+ 2 3)
=> 5

Возможность представления исполняемого кода с использованием структуры данных на языке открывает новую возможность - мы можем писать программы, которые пишут программы. Это означает, что расширения, требующие изменений в компиляторе и системе исполнения на других языках, могут быть реализованы в Lisp, как несколько строк самого Lisp. Представьте себе, что вам нужна новая структура управления на вашем языке под названием when. Он похож на if, но делает код чтения немного более естественным в некоторых ситуациях:

 (when this-is-true do-this)

Вы можете расширить свою систему Lisp, чтобы поддерживать when, написав короткий макрос:

 (defmacro when (condition &rest body)
    `(if ,condition (progn ,@body)))

Макрос - это не что иное, как список, который расширяется во время компиляции. Более сложные языковые структуры или даже целые парадигмы могут быть добавлены на основной язык, используя такие списки. Например, CLOS, Common Lisp Object Systems - это в основном набор макросов, написанных в Common Lisp.

Ответ 6

Если вы не используете что-то вроде старого Harvard Mark I, ваш код хранится в том же месте и в соответствии с вашими данными - просто (как вы отметили), вероятно, в виде символов ASCII, поэтому с этим действительно сложно что-либо сделать. Скорее всего, большинство программистов Java никогда не анализировали Java-код самостоятельно.

Посмотрите на любую программу - там огромное количество информации (ну, в зависимости от программы!), закодированной в самом исходном коде. Это его причина для существования! Не используя гомокиноязычный язык, вы неявно говорите, что вы в порядке, не имея возможности прочитать это из другой программы, которую вы пишете (или что это так, что это так сложно, что никто никогда не будет). В принципе, единственной программой на вашем компьютере, которая может ее прочитать, является компилятор, и единственное, что он может сделать после чтения, - генерировать объектный код и сообщения об ошибках.

Представьте, что вам приходилось работать с каким-то другим источником данных каждый день, например, с файлами XML или RDBMS, и что единственный способ получить доступ к этим данным - это запустить его через "компилятор", который преобразил его в формат, который вы могли бы прочитать, Я не думаю, что кто-то будет спорить, что это хорошая идея.: -)

Я действительно не знаю, где я собираюсь с этим, поэтому я постараюсь обобщить мои вышеизложенные промахи:

  • Я вижу код-данные как просто следующий логический шаг от архитектуры Гарварда до архитектуры фон Неймана
  • У нас уже есть X-as-data для почти любого другого X, поэтому кажется странным исключать один вид данных, которые программисты тратят на весь день.