В информатике язык динамического программирования является классом языки программирования высокого уровня, которые в среде выполнения выполняют множество общих программных действий, которые статические языки программирования выполняют во время компиляции. Эти варианты поведения могут включать расширение программы путем добавления нового кода , расширения объектов и определений или изменения системы типов . Хотя подобное поведение можно эмулировать практически на любом языке, с разной степенью сложности, сложности и затрат на производительность, динамические языки предоставляют прямые инструменты для их использования. Многие из этих функций были впервые реализованы как встроенные функции в языке программирования Lisp.
Большинство динамических языков также динамически типизированы, но не все. Динамические языки часто (но не всегда) называются языками сценариев, хотя этот термин в самом узком смысле относится к языкам, специфичным для данной среды выполнения.
Некоторые динамические языки предлагают функцию eval. Эта функция принимает строковый параметр, содержащий код на языке, и выполняет его. Если этот код обозначает выражение, возвращается результирующее значение. Однако Эрик Мейер и Питер Дрейтон предлагают программистам «использовать eval как замену беднякам для функций высшего порядка."
Система типов или объектов может обычно изменяются во время выполнения на динамическом языке. Это может означать создание новых объектов из определения среды выполнения или на основе миксинов существующих типов или объектов. Это также может относиться к изменению наследования или дерево типов, тем самым изменяя поведение существующих типов (особенно в отношении вызова методов ).
Отражение распространено во многих динамических языках, и обычно включает анализ типов и метаданных общих или полиморфных данных. Однако он может также включать полную оценку и модификацию кода программы в виде данных, таких как функции что Lisp предоставляет для анализа S-выражений.
Ограниченное количество языков динамического программирования pro См. функции, которые сочетают в себе интроспекцию кода (возможность исследовать классы, функции и ключевые слова, чтобы знать, что они собой представляют, что они делают и что они знают) и eval в функции, называемой макросами. Большинство современных программистов, знакомых с термином «макрос», встречали их в C или C ++, где они представляют собой статическую функцию, встроенную в небольшое подмножество языка и способную только строковых подстановок в тексте программы. Однако в динамических языках они обеспечивают доступ к внутренней работе компилятора и полный доступ к интерпретатору, виртуальной машине или среде выполнения, позволяя определять языковые конструкции, которые могут оптимизировать код или изменять синтаксис или грамматику язык.
Сборка, C, C ++, ранняя Java и Fortran обычно не попадают в эту категорию.
В следующих примерах показаны динамические функции с использованием языка Common Lisp и его объектной системы Common Lisp (CLOS).
В примере показано, как функция может быть изменена во время выполнения из вычисленного исходного кода
; исходный код хранится как данные в переменной CL-USER>(defparameter * best-guess-formula * '(lambda (x) (* x x 2.5))) * BEST-GUESS-FORMULA *; функция создается из кода и компилируется во время выполнения, функция доступна под именем best-guess CL-USER>(compile 'best-guess * best-guess-formula *) #; функцию можно назвать CL-USER>(предположение 10.3) 265.225; исходный код может быть улучшен во время выполнения CL-USER>(setf * best-guess-formula * `(lambda (x), (list 'sqrt (third * best-guess-formula *)))) (LAMBDA (X) (КОРЕНЬ (* XX 2,5))); компилируется новая версия функции CL-USER>(compile 'best-guess * best-guess-formula *) # ; следующий вызов вызовет новую функцию, функцию позднего связывания CL-USER>(предположение 10.3) 16.28573
В этом примере показано, как можно изменить существующий экземпляр, чтобы включить новый слот при изменении его класса и что существующий метод может быть заменен новой версией.
; человек класс. У человека есть имя. CL-USER>(defclass person () ((имя: initarg: имя))) #; пользовательский метод печати для объектов класса person CL-USER>(defmethod print-object ((p person) stream) (print-unreadable-object (p stream: type t) (format stream "~ a" (slot-value) p 'имя)))) # ; один пример экземпляра человека CL-USER>(setf * person-1 * (make-instance 'person: name «Ева Луатор»)) # ; классный человек получает второй слот. Затем у него есть имя и возраст слота. CL-USER>(defclass person () ((name: initarg: name) (age: initarg: age: initform: unknown))) # ; обновление метода для печати объекта CL-USER>(defmethod print-object ((p person) stream) (print-unreadable-object (p stream: type t) (format stream "~ a age: ~" (значение слота) p 'имя) (значение слота p' возраст)))) # ; существующий объект теперь изменен, у него есть дополнительный слот и новый метод печати CL-USER>* person-1 * # ; мы можем установить новый возрастной интервал экземпляра CL-USER>(setf (slot-value * person-1 * 'age) 25) 25; объект обновлен. CL-USER>* person-1 * #
В следующем примере класс person-1 получает новый суперкласс. Метод print переопределяется таким образом, что он объединяет несколько методов в эффективный метод. Эффективный метод собирается на основе класса аргумента и доступных и применимых методов во время выполнения.
; класс person CL-USER>(defclass person () ((name: initarg: name))) #; человек просто печатает свое имя CL-USER>(defmethod print-object ((p person) stream) (print-unreadable-object (p stream: type t) (форматный поток "~ a" (имя слота p ')))) # ; экземпляр человека CL-USER>(defparameter * person-1 * (make-instance 'person: name "Eva Luator")) * PERSON-1 *; отображение экземпляра человека CL-USER>* person-1 * # ; теперь переопределяем метод печати, чтобы он был расширяемым; метод around создает контекст для метода печати и вызывает следующий метод CL-USER>(defmethod print-object: around ((p person) stream) (print-unreadable-object (p stream: type t) (call- следующий-метод))) # ; основной метод выводит имя CL-USER>(defmethod print-object ((p person) stream) (формат потока "~ a" (значение слота p 'name))) # ; новый класс id-mixin предоставляет идентификатор CL-USER>(defclass id-mixin () ((id: initarg: id))) # ; метод печати просто печатает значение идентификатора слота CL-USER>(defmethod print-object: after ((object id-mixin) stream) (формат потока "ID: ~ a" (id объекта значения слота))) # ; теперь мы переопределяем класс person, чтобы включить миксин id-mixin CL-USER 241>(defclass person (id-mixin) ((name: initarg: name))) # ; у существующего экземпляра * person-1 * теперь есть новый слот, и мы устанавливаем его на 42 CL-USER 242>(setf (slot-value * person-1 * 'id) 42) 42; отображение объекта снова. У функции print-object теперь есть эффективный метод, который вызывает три метода: метод around, основной метод и метод after. CL-USER 243>* person-1 * #
Популярные языки динамического программирования включают JavaScript, Python, Ruby, PHP, Lua и Perl. Следующие языки обычно считаются динамическими языками:
(Многие используют термин «языки сценариев».)