Язык программирования для самоизменяющегося кода?
- Я недавно думаю о написании самоизменяющихся программ, я думаю, что это может быть мощно и весело. Поэтому в настоящее время я ищу язык, который позволяет легко изменять собственный код программы.
- Я читал о C# (как способ обойти) и способности компилировать и выполнять код во время выполнения, но это слишком больно.
- Я тоже думаю о сборке. Там легче изменить исполняемый код, но он не очень мощный (очень сырой).
Можете ли вы предложить мощный язык или функцию, которая поддерживает изменение кода во время выполнения?
пример
Вот что я имею в виду, изменяя код во время выполнения:
Start:
a=10,b=20,c=0;
label1: c=a+b;
....
label1= c=a*b;
goto label1;
и может составлять список инструкций:
code1.add(c=a+b);
code1.add(c=c*(c-1));
code1. execute();
14 ответов
Malbolge будет хорошим местом для начала. Каждая инструкция является самоизменяющейся, и с ней очень весело (*) играть.
(*) Отказ от ответственности: на самом деле не может быть весело.
Я очень рекомендую Лисп. Данные о Лиспе могут быть прочитаны и исполнены как код. Лисп-код может быть записан как данные.
Он считается одним из канонических самоизменяемых языков.
Пример списка (данных):
'(+ 1 2 3)
или, вызывая данные как код
(eval '(+ 1 2 3))
запускает функцию +
Вы также можете войти и редактировать членов списков на лету.
редактировать:
Я написал программу для динамического генерирования программы и оценки ее на лету, а затем доложил мне, как она работала по сравнению с базовой линией (обычное сообщение делится на 0, га).
Пока что каждый ответ касается компиляции в режиме отражения / выполнения, но в комментариях, которые вы упомянули, вас интересует реальный самоизменяющийся код - код, который изменяет себя в памяти.
Нет никакого способа сделать это в C#, Java или даже (переносимо) в C - то есть вы не можете изменять загруженный двоичный файл в памяти, используя эти языки.
В общем, единственный способ сделать это с помощью сборки, и это сильно зависит от процессора. На самом деле, она также сильно зависит от операционной системы: для защиты от полиморфных вирусов большинство современных операционных систем (включая Windows XP+, Linux и BSD) используют W ^ X, что означает, что вам приходится сталкиваться с некоторыми трудностями при написании полиморфных исполняемых файлов в те операционные системы, для тех, которые позволяют это вообще.
На некоторых интерпретируемых языках может быть возможно, чтобы программа изменила свой собственный исходный код во время работы. Однако Perl, Python (см. Здесь) и все известные мне реализации Javascript не позволяют этого.
Могу ли я предложить Python, хороший динамический язык очень высокого уровня, который включает в себя богатый самоанализ (и, например, с использованием compile
, eval
или же exec
разрешает форму самоизменяющегося кода). Очень простой пример, основанный на вашем вопросе:
def label1(a,b,c):
c=a+b
return c
a,b,c=10,20,0
print label1(a,b,c) # prints 30
newdef= \
"""
def label1(a,b,c):
c=a*b
return c
"""
exec(newdef,globals(),globals())
print label1(a,b,c) # prints 200
Обратите внимание, что в приведенном выше примере кода c
изменяется только в области действия функции.
Common Lisp был разработан с такой целью. Вы также можете попробовать Smalltalk, где использование отражения для изменения запущенного кода не является неизвестным.
В обоих этих языках вы, вероятно, будете заменять целую функцию или весь метод, а не одну строку кода. Методы Smalltalk, как правило, более детализированы, чем функции Lisp, так что это может быть хорошим началом.
Лично я нахожу довольно странным, что вы находите сборку легче в обращении, чем C#. Мне кажется еще более странным, что вы думаете, что сборка не такая мощная: вы не можете получить ничего более мощного, чем необработанный машинный язык. Во всяком случае, каждому свое.
В C# есть отличные службы рефлексии, но если у вас есть отвращение к этому. Если вы действительно знакомы с C или C++, вы всегда можете написать программу, которая пишет C/C++ и выдает ее компилятору. Это было бы целесообразно только в том случае, если ваше решение не требует быстрой перезаписи времени перезаписи (порядка десятков секунд и более).
Javascript и Python также поддерживают рефлексию. Если вы думаете об изучении нового, интересного языка программирования, который является мощным, но не требует больших технических затрат, я бы предложил Python.
Многие языки позволяют оценивать код во время выполнения.
- шепелявость
- Perl
- питон
- PHP
- Рубин
- Groovy (через GroovyShell)
В высокоуровневых языках, где вы компилируете и выполняете код во время выполнения, это на самом деле не самоизменяющийся код, а динамическая загрузка классов. Используя принципы наследования, вы можете заменить класс Factory и изменить поведение приложения во время выполнения.
Только на ассемблере у вас действительно есть истинное само-изменение, записывая непосредственно в сегмент кода. Но для этого мало практического применения. Если вам нравится вызов, напишите самошифрующийся, возможно, полиморфный вирус. Это было бы весело.
Я иногда, хотя и очень редко, занимаюсь самоизменением кода в Ruby.
Иногда у вас есть метод, в котором вы на самом деле не знаете, правильно ли инициализируются используемые вами данные (например, ленивый кеш) или нет. Итак, вы должны проверить в начале вашего метода, правильно ли инициализированы данные, а затем, возможно, инициализировать их. Но вам действительно нужно выполнить эту инициализацию только один раз, но вы проверяете ее каждый раз.
Поэтому иногда я пишу метод, который выполняет инициализацию, а затем заменяет себя версией, не содержащей код инициализации.
class Cache
def [](key)
@backing_store ||= self.expensive_initialization
def [](key)
@backing_store[key]
end
@backing_store[key]
end
end
Но, честно говоря, я не думаю, что это того стоит. На самом деле, мне стыдно признаться, что я на самом деле никогда не тестировал, чтобы увидеть, действительно ли это условие имеет какое-то значение. (В современной реализации Ruby с агрессивно оптимизирующим JIT-компилятором, управляемым обратной связью, вероятно, нет.)
Обратите внимание, что в зависимости от того, как вы определяете "самоизменяющийся код", это может быть или не быть тем, что вы хотите. Вы заменяете какую-то часть выполняемой в данный момент программы, так что…
РЕДАКТИРОВАТЬ: Теперь, когда я думаю об этом, эта оптимизация не имеет особого смысла. В любом случае дорогая инициализация выполняется только один раз. Единственное, чего избегает модификация, это условно. Было бы лучше взять пример, когда сам чек дорог, но я не могу вспомнить ни одного.
Однако я подумал о замечательном примере самоизменяющегося кода: Maxine JVM. Maxine - это исследовательская виртуальная машина (технически ее нельзя назвать "JVM", потому что ее разработчики не запускают тесты совместимости), полностью написанную на Java. Теперь, есть много JVM, написанных сами по себе, но Maxine - единственная, о которой я знаю, которая также работает сама по себе. Это очень сильно. Например, JIT-компилятор может JIT-компилировать сам, чтобы адаптировать его к типу кода, который JIT-компилирует.
Очень похожая вещь происходит в KleinVM, которая является VM для языка самопрограммирования.
В обоих случаях виртуальная машина может оптимизировать и перекомпилировать себя во время выполнения.
Я написал Python класс Code, который позволяет вам добавлять и удалять новые строки кода в объекте, печатать код и выполнять его. Код класса показан в конце.
Пример: если x == 1, код меняет свое значение на x = 2, а затем удаляет весь блок с условным условием, которое проверено на это условие.
#Initialize Variables
x = 1
#Create Code
code = Code()
code + 'global x, code' #Adds a new Code instance code[0] with this line of code => internally code.subcode[0]
code + "if x == 1:" #Adds a new Code instance code[1] with this line of code => internally code.subcode[1]
code[1] + "x = 2" #Adds a new Code instance 0 under code[1] with this line of code => internally code.subcode[1].subcode[0]
code[1] + "del code[1]" #Adds a new Code instance 0 under code[1] with this line of code => internally code.subcode[1].subcode[1]
После создания кода вы можете распечатать его:
#Prints
print "Initial Code:"
print code
print "x = " + str(x)
Выход:
Initial Code:
global x, code
if x == 1:
x = 2
del code[1]
x = 1
Выполнить кейд, вызвав объект: code()
print "Code after execution:"
code() #Executes code
print code
print "x = " + str(x)
Выход 2:
Code after execution:
global x, code
x = 2
Как видите, код изменил переменную x на значение 2 и удалил весь блок if. Это может быть полезно, чтобы избежать проверки условий после их выполнения. В реальной жизни этот сценарий может быть обработан системой сопрограмм, но этот эксперимент с самоизменяющимся кодом - просто для удовольствия.
class Code:
def __init__(self,line = '',indent = -1):
if indent < -1:
raise NameError('Invalid {} indent'.format(indent))
self.strindent = ''
for i in xrange(indent):
self.strindent = ' ' + self.strindent
self.strsubindent = ' ' + self.strindent
self.line = line
self.subcode = []
self.indent = indent
def __add__(self,other):
if other.__class__ is str:
other_code = Code(other,self.indent+1)
self.subcode.append(other_code)
return self
elif other.__class__ is Code:
self.subcode.append(other)
return self
def __sub__(self,other):
if other.__class__ is str:
for code in self.subcode:
if code.line == other:
self.subcode.remove(code)
return self
elif other.__class__ is Code:
self.subcode.remove(other)
def __repr__(self):
rep = self.strindent + self.line + '\n'
for code in self.subcode: rep += code.__repr__()
return rep
def __call__(self):
print 'executing code'
exec(self.__repr__())
return self.__repr__()
def __getitem__(self,key):
if key.__class__ is str:
for code in self.subcode:
if code.line is key:
return code
elif key.__class__ is int:
return self.subcode[key]
def __delitem__(self,key):
if key.__class__ is str:
for i in range(len(self.subcode)):
code = self.subcode[i]
if code.line is key:
del self.subcode[i]
elif key.__class__ is int:
del self.subcode[key]
Вы можете сделать это в Maple (язык компьютерной алгебры). В отличие от многих приведенных выше ответов, в которых используются скомпилированные языки, которые позволяют создавать и ссылаться в новом коде только во время выполнения, здесь вы можете честно изменить код работающей в данный момент программы. (Ruby и Lisp, как отмечают другие авторы, также позволяют вам делать это; возможно, Smalltalk тоже).
На самом деле, в Maple стандартно было то, что большинство функций библиотеки были небольшими заглушками, которые при первом вызове загружали свое "настоящее" Я с диска, а затем самостоятельно изменяли себя до загруженной версии. Это больше не так, поскольку загрузка библиотеки была виртуализирована.
Как указали другие: для этого вам нужен интерпретируемый язык с сильными возможностями для размышлений и овеществления.
Я написал автоматический нормализатор / упрощитель для кода Maple, который я начал выполнять для всей библиотеки (включая себя); и поскольку я не был слишком осторожен во всем своем коде, нормализатор изменил сам себя. Я также написал Partial Evaluator (недавно принятый SCP) под названием MapleMIX - доступный на sourceforge - но не мог полностью применить его к себе (это не было целью разработки).
Реализация LLVM в Dlang содержит атрибуты функций @dynamicCompile и @dynamicCompileConst, позволяющие компилировать в соответствии с инструкциями собственного хоста, установленными во время компиляции, и изменять константы времени компиляции во время выполнения посредством перекомпиляции.
https://forum.dlang.org/thread/bskpxhrqyfkvaqzoospx@forum.dlang.org
В Lua вы можете "зацепить" существующий код, что позволяет вам присоединять произвольный код к вызовам функций. Это выглядит примерно так:
local oldMyFunction = myFunction
myFunction = function(arg)
if arg.blah then return oldMyFunction(arg) end
else
--do whatever
end
end
Вы также можете просто переключаться между функциями, что дает вам самоизменяющийся код.
Вы смотрели на Java? Java 6 имеет API-интерфейс компилятора, поэтому вы можете написать код и скомпилировать его в Java VM.
В MICGBF он изменяет интерпретатор каждый раз, когда вы его запускаете.
Это также очень весело * для программирования.
* Отказ от ответственности: это может быть не очень интересно программировать.