首页 文章

同构和“不受限制”的自我修改代码lisp是否真的可以自我修改?

提问于
浏览
25

我将继续承认我对Lisp的了解非常少 . 但是我对这门语言非常感兴趣,并计划在不久的将来开始认真学习它 . 我对这些问题的理解无疑是有缺陷的,所以如果我说出任何有些错误的东西,请评论并纠正我而不是贬低 .

Truly Homoiconic and Self-modifiable languages

我正在寻找支持Homoiconicity(代码与数据具有相同表示)和 unrestricted 自修改的编程语言示例(不受限制意味着您可以更改正在运行的代码的每个方面,而不仅仅是发出新代码或更改函数指针/代表) .

到目前为止我发现的三个例子符合这个标准:

  • 机器代码 . Homoiconic在一切都是一个数字 . 无限制地可修改,因为它包含指针,可用于操纵任何内存地址,无论该地址是保存代码还是数据 .

  • Malbolge . 与机器代码相同的推理 . 每条指令在执行后都会自行修改

  • DNA . 不是编程语言,但仍然很有趣 . 它不是以前的状态(有副作用,如辐射时不时地拧紧它) . 无论如何,这只是一种间接的自我修改方式 . 简而言之,DNA可以自我修饰,但它通过与其相关的突变一起繁殖其自身来实现 . DNA的物理串是"immutable" .

Why Lisp is not on this list

Lisp不在那个列表中,因为在我看来,Lisp几乎只是Homoiconic,并且只支持受限制的自我修改 . 你可以做点什么

(+ 1 2 3)

这将做同样的事情

(eval '(+ 1 2 3))

在第一个版本中, (+ 1 2 3) 是原始代码,而在第二个版本中,它是数据 . 通过假设这个陈述的真实性,可以认为Lisp不必明确标记哪些数字是指令,哪些是指针,哪些是数据 . 在实际需要解释之前,一切都只是一个数字,此时它可以是任何一个 .

对于不受限制的自我修改,这是一个更强大的案例 . 当然,您可以获取代表某些代码并对其进行操作的列表 . 例如改变

'(+ 1 2 3)

'(+ 1 4 3)

然后你通过 eval 运行它 . 但是当你这样做时,'re just compiling some code and running it. You'不会修改现有代码,你只是发出并运行新代码 . C#可以使用表达式树完全相同的东西,即使是一种不太方便的格式(由于C#代码与其AST的表示形式不同而产生,而不是Lisp,这是它自己的AST) . 您是否可以实际获取整个源文件并在运行时开始修改整个源文件,对源文件所做的更改会对程序行为产生实时影响?

除非有某种方法可以做到这一点,否则Lisp既不是讽刺也不是自我修改 . (为了推断定义的争论,Lisp不是同源的,也不是自我修改到与机器代码相同的程度 . )

Ways to make Lisp Homoiconic/Unrestrictedly self-modifiable

我可以看到3种可能的方法使Lisp像机器代码一样可以单调/自我修改 .

  • Non-Von-Neumann architecture. 如果有人能发明一些令人惊奇的假设机器,其中程序的最低级别表示是一个可以直接执行的AST(不需要进一步编译) . 在这样的机器上,AST将代表可执行指令以及数据 . 不幸的是,问题赢了_243304改变了这一点 . 在机器代码中,您可以根据需要在代码和数据之间来回切换 . 而使用eval和Lisp一旦你没有办法再将该列表作为数据返回 . 事实上,这个列表已经永远消失了,并且已经被它取代了's value. We'缺少一些至关重要的东西,这恰好是指针 .

  • List Labels. 如果要求每个列表都有唯一标签,则可以通过对具有给定标签的列表运行函数来进行间接自修改 . 结合延续,这最终将允许自动修改代码与机器代码具有相同的意义 . 标签与机器代码存储器地址等效 . 例如,考虑一个Lisp程序,其中AST的顶部节点具有标签"main" . 在main中你可以执行一个函数,它接受一个标签,一个Integer,一个Atom,并将原子复制到List,其标签与提供给函数的标签相匹配,在Integer指定的索引处 . 然后在main上调用当前的continuation . 你去,自我修改代码 .

  • Lisp Macros. 我没想到 .

点1.结合2.将产生一个完全自我修改的Lisp . 前提是可以 生产环境 所描述的神奇的Lisp机器 . 2.单独可以产生自我修改的Lisp,但是在冯·诺依曼架构上的实现可能非常低效 .

The Questions

  • 机器码,dna和malbolge以外的任何语言都可以完全自我修改并且是同音的吗?

  • (DO NOT bother answering if you did a tl;dr at the above text) . lisp真的是homoiconic自我修改?如果你这样说,你能准确引用我在论证中误入歧途的地方吗?

Appendix

语言具有不受限制的自我修改但不具有杀戮性

  • 大会 . 代码使用单词而不是数字,所以失去了杀戮,但它仍然有指针,它保留了对内存的完全控制,允许不受限制的自我修改 .

  • 任何使用原始指针的语言 . 例如C / C / Objective C.与Assembly相同的论点

  • 包含虚拟指针的JIT语言 . 例如,C#/ .net在不安全的上下文中运行 . 与Assembly相同的论点 .

其他可能以某种方式相关/有趣的概念和语言:Lisp,Ruby,Snobol,Forth和它的编译时元编程,Smalltalk和它的反射,无类型的lambda演算,它的属性是一切都是函数(假设我们假设我们可以发明一台直接执行lambda演算的机器,lambda演算将是homoiconic,并且Von Neumann机器代码在所述机器上运行时不会 . [并且Godels定理将是可执行的 . 哈哈,可怕的想法:P])

4 回答

  • 2

    Lisp是一系列编程语言 . 这个家庭的成员在能力和实施技术上有很大的不同 . 对此有两种不同的解释:

    • Lisp是一系列共享一些重叠功能集的语言 . 这包括从第一个Lisp到Maclisp,Scheme,Logo,Common Lisp,Clojure以及数百种不同语言及其实现的所有内容 .

    • Lisp还有一个主要的语言分支,其名称中也大多数都有'Lisp':Lisp,MacLisp,Common Lisp,Emacs Lisp,......

    随着时间的推移,已经投入了大量的研究来改进语言(使其更具“功能性”或使其更加面向对象,或两者兼而有之)并改进实现技术 .

    例如,Common Lisp支持编译,各种优化等,允许开发人员在需要在灵活性和功能之间取得 balancer 的大型项目中使用它 . 编译后的函数就是机器代码,不再是由列表,符号,字符串和数字组成的数据结构 .

    Common Lisp允许实现创建静态代码 . 同时,它为受控运行时修改留下了一些空间(例如,通过使用运行时编译器,加载代码,评估代码,替换代码......) .

    OTOH如果你有一个带解释器的Lisp实现,另外解释器可能使用Lisp数据结构来表示源,那么你就可以改变运行程序,例如通过改变列表结构 . 有Lisp方言的实现允许这样做 . 一个典型的事情是使用宏,这些宏可以在运行时由这样的系统计算 . 其他一些Lisps有所谓的Fexprs,它们是一种类似的机制(但通常无法有效编译) .

    在基于Common Lisp的应用程序(例如,用Lisp编写的CAD系统)中,许多源信息已被传送工具删除,这是不可能的 . 人们可以拥有一个机器代码的可执行文件,它可以消除大部分的运行时灵活性 .

    同质性也不是一个非常明确的概念 . 对于Lisp我更喜欢我们说源代码可以转换为数据,因为源代码使用s表达式的外部表示,这是一种数据序列化格式 . 但并非每个s表达式都是有效的Lisp程序 . 此外,作为Lisp数据的源代码的内部表示在任何方面都不是“标志性的” . Lisp将源代码作为外部s表达式,在阅读源代码后,它具有Lisp数据的内部表示 . READ读取它,PRINT打印它,EVAL评估它 .

    在Lisp中还有其他方法可以提供对运行程序和程序的Lisp的访问:

    • CLOS中的元对象协议(Common Lisp Object System)就是这样一个例子

    • 3Lisp提供了无限的解释塔 . 有一个运行程序的Lisp解释器 . 这个Lisp解释器是由另一个Lisp解释器运行的,它再次在另一个解析器中运行,那个也是......

  • 19

    在第一个版本(1 2 3)中是原始代码,而在第二个版本中它是数据 . 通过假设这个陈述的真实性,可以说Lisp甚至不是杀人的 . 代码与数据具有相同的表示,因为它们都是列表/树/ S表达式 . 但事实上,你必须明确标记这些列表/树/ S表达式中的哪些是代码,哪些是我的数据似乎说Lisp毕竟不是杀人的 .

    这不是真的 . 在第一个版本中,作为数据的列表 (+ 1 2 3) 被馈送到要执行的解释器,即被解释为代码 . 事实你必须将s表达式标记为特定上下文中的代码或数据,这不会使Lisp非同质 .

    同质性的观点是所有程序都是数据,而不是所有数据都是程序,因此两者之间仍然存在差异 . 在Lisp中, (1 2 3) 是有效列表但不是有效程序,因为整数不是函数 .

    [如果我们看一下另一个伟大的homoiconic编程语言Prolog,那么我们就会看到同样的现象:我们可以构建一个数据结构 foo(X, 1, bar) ,但是如果没有 foo 的定义,我们就无法执行它 . 此外,变量不能是谓词或事实的名称,因此 X. 永远不是有效的程序 .

    Lisp在很大程度上是自我修改的 . 例如,以下是如何更改函数的定义:

    [1]> (defun foo (x) (+ x 1))
    FOO
    [2]> (defun bar (x) (+ x 2))
    BAR
    [3]> (setf (symbol-function 'foo) #'bar)
    #<FUNCTION BAR (X) (DECLARE (SYSTEM::IN-DEFUN BAR)) (BLOCK BAR (+ X 2))>
    [4]> (foo 3)
    5
    

    说明:在 [1] ,我们将函数 foo 定义为add-1函数 . 在 [2] ,我们将 bar 定义为add-2函数 . 在 [3] ,我们将 foo 重置为add-2函数 . 在 [4] ,我们看到我们已经成功修改了 foo .

  • 2

    我在这里作为一个粉丝遇到了,如果你想了解Lisp Macros,我就是've said it before, but read Paul Graham' s On Lisp . 他们在这里区分Lisp语言系列,给定的Lisp和给定的Lisp实现很重要 .

    我认为我对你的论点采取的主要问题出现在“为什么Lisp不在此列表中”之后的第一段中,并且与Lisp中的REPL有关 . 当您将exp(1 2 3)输入解释器时,实际上您正在调用列表中的函数EVAL(1 2 3) . 您描述的“原始代码”实际上是“数据”,被提供给其他lisp代码,它只是特定上下文中的数据 .

  • 11

    宏可以避免引用,如果这是你所追求的:

    > (defmacro foo (x) (cdr x))
    > (foo (+ - 5 2))
    3
    

    (+ - 5 2) 代码还是数据?在写入时它似乎是数据 . 在宏扩展时间之后,它看起来像代码 . 如果 foo 的定义在其他地方,它可以很容易地(错误地)被认为是一个函数,在这种情况下, (+ - 5 2) 将被视为行为类似于代码行为的代码 .

相关问题