【问题标题】:Can a compiled language be homoiconic?编译语言可以是谐音的吗?
【发布时间】:2010-11-17 08:29:44
【问题描述】:

根据定义,homiconic这个词的意思是:

代码和数据的相同表示

在 LISP 中,这意味着您可以有一个带引号的列表并对其进行评估,因此 (car list) 将是函数,(cdr list) 是参数。这可以在编译时或运行时发生,但是它需要一个解释器。

没有编译时解释器的编译语言是否也可以是同音的?还是同音性的概念仅限于口译员?​​

【问题讨论】:

  • 作为一个问题,您认为 Perl 是同音字吗?它可以将自己的代码表示为字符串,并具有 eval() 函数。
  • 这是我的想象,还是我们对此页面上的每个答案都投了反对票?
  • David:除非您将所有 Perl 数据存储在字符串中,并使用 eval() 调用所有 Perl 函数,否则不,我不会认为 Perl 是同义词。 :-)
  • 程序集是同形的。

标签: compiler-construction lisp scheme interpreter homoiconicity


【解决方案1】:

编译只是优化解释。解释器获取表示代码的一段数据,然后“执行”该代码:代码的含义变成了执行路径,数据通过解释器的内部流动。编译器获取相同的数据,将其转换为另一种形式,然后将其传递给另一个解释器:一个在硅 (CPU) 中实现,或者可能是一个假的(虚拟机)。

这就是为什么一些 Lisp 实现不能有解释器的原因。 EVAL 函数可以编译代码然后分支到它。 EVAL 和 COMPILE 不必具有不同的操作模式。 (Clozure、Corman Lisp、SBCL 是“仅编译器”Lisp 的示例。)

开头的数据部分是语言同音的关键,而不是代码的执行是否经过编译优化。 “代码就是数据”是指“源代码代码就是数据”而不是“可执行代码就是数据”。 (当然可执行代码是数据,但我们所说的数据是指我们想要操作的代码的绝对首选表示。)

【讨论】:

    【解决方案2】:

    机器码本身是同形的,所以是的。

    数据或指令只是语义问题(也许是它们所在的内存)。

    【讨论】:

      【解决方案3】:

      Lisp 通常被编译。已经有使用 JIT 编译器而不是解释器的实现。

      因此,对于代码即数据语言,没有必要拥有解释器(在“不是编译器”的意义上)。

      【讨论】:

        【解决方案4】:

        是的;您只需将编译器的副本粘贴到语言运行时中。 Chez Scheme 是众多优秀的编译器之一。

        【讨论】:

          【解决方案5】:

          'Homoiconic' 是一种模糊的结构。 'code is data' 更清楚一点。

          无论如何,维基百科上关于 Homoiconic 的第一句话并没有那么糟糕。它说该语言必须使用其数据结构具有源表示。如果我们忘记将“字符串”作为源代码表示(这是微不足道的,并且对有用的概念“同音符号”没有多大帮助),那么 Lisp 有用于表示源代码的列表、符号、数字、字符串等。 EVAL 函数的接口决定了该语言正在处理什么样的源表示。在这种情况下,Lisp,它不是字符串。 EVAL 期望通常的各种数据结构,并且 Lisp 的评估规则确定字符串评估为自身(因此不会被解释为程序表达式,而只是字符串数据)。一个数字也会对自己进行评估。列表(sin 3.0)是一个符号和一个数字的列表。评估规则说,这个带有表示函数的符号的列表作为第一个对象将被评估为函数应用程序。对于数据、特殊运算符、宏应用程序和函数应用程序,有一些类似的评估规则。就是这样。

          为了清楚起见:在 Lisp 中,函数 EVAL 是在 Lisp 数据结构上定义的。它需要一个数据结构,根据其评估规则对其进行评估并返回结果 - 再次使用其数据结构。

          这符合同音符号的定义:源代码具有使用 Lisp 数据类型的本机表示。

          现在,有趣的部分是:EVAL 的实现方式无关紧要。重要的是它接受使用 Lisp 数据结构的源代码,执行代码并返回结果。

          所以EVAL 使用编译器是完全合法的

          (EVAL code)  =  (run (compile-expression code))
          

          这就是几个 Lisp 系统的工作方式,有些甚至没有解释器。

          因此,“Homoiconic”表示源代码具有数据表示。它并不是说在运行时必须解释此源代码或执行基于此源代码。

          如果代码被编译,则在运行时既不需要编译器也不需要解释器。只有当程序想要在运行时评估或编译代码时才需要这些——这通常是不需要的。

          Lisp 还提供了一个原始函数 READ,它将数据的外部表示(S-Expressions)转换为数据的内部表示(Lisp 数据)。因此,它也可用于将源代码的外部表示转换为源代码的内部表示。 Lisp 没有对源代码使用特殊的解析器——因为代码是数据,所以只有 READ。

          【讨论】:

          • “同音/同义”有什么模糊之处?确切的维基百科引用说:“在计算机编程中,同音性是某些编程语言的属性,其中程序的主要表示也是语言本身原始类型的数据结构,从同音意思相同和图标意思表示”。话虽如此,您正确地指出这不是一个适用于讨论编译器和解释器的概念。使用一种语言“做什么”不同于该语言的结构。
          • 'String' 没有意义,因为所有基于文本的编程语言都有字符串表示。在 Java 中,您可以构建一个字符串,将其写出来编译它,然后使用类加载器加载结果。这会让 Java 变得“同音”吗?如果是,那么“同音性”的整个概念都是无用的。 'homoiconic' 可能想要捕捉的想法是代码具有结构化数据的内部表示(-> 不是纯字符串)。
          • 并且这些内部数据结构本身(不是代码,只是一种数据格式)具有外部表示,也用于编写程序。 Lisp:Lisp 的内部表示是 Lisp 数据。 Lisp 数据具有作为 S 表达式的外部表示。 S-Expressions 也用于程序代码。 -> 谐音
          • @Rainer Joswig - 感谢您的澄清。所以,真的,你对“表示”这个词的模棱两可以及强烈的“视觉”对“同音”的含义感到不满。如果“同音”的定义指定“内部表征”,你会更高兴,还是“同音”对你来说过于强烈地强加了视觉体验隐喻?我问这个问题是因为我经常发现技术/科学主题的讨论被不必要的“流血”词所笼罩,所以我喜欢收集它的例子来思考这个问题。
          • @Pinochle,对,如果你在其他情况下看“标志性”,它会将形式和意义联系起来。这在编程语言的上下文中都是模糊的,“同音性”的具体定义也很模糊,“同音性”一词所暗示的含义与描述并不真正匹配。 “图标”与外部形式、符号或视觉类比有关。 Lisp 中有趣的部分是 Lisp 数据具有内部和外部表示,并且这两种表示都是编码源代码的主要手段(外部作为 s 表达式,内部作为 Lisp 数据结构)。
          【解决方案6】:

          在最字面的形式中,C 是谐音的。您可以使用&functionName 访问函数的表示,并使用somePtrCastToFnPtr(SomeArgs) 执行数据。然而,这是在机器代码级别,如果没有某种库支持,您会发现它很难使用。某种可嵌入的编译器(我似乎记得 LLVM 可以做到这一点)会使它更实用。

          【讨论】:

            【解决方案7】:

            问题在于很多处理器将指令区和数据区分开,并主动阻止程序修改自己的代码。这种代码过去被称为“退化代码”,被认为是非常糟糕的事情。

            解释器(和虚拟机)没有这个问题,因为它们可以将整个程序视为数据,唯一的“代码”就是解释器。

            【讨论】:

              【解决方案8】:

              在我看来这是一个奇怪的问题:

              首先,同音部分是呈现给程序员的界面。语言的重点在于它们抽象了一个较低级别的功能,该功能保留了与较高级别表示相同的语义(尽管方式不同)。

              dsm 的机器码点很好,但提供:

              1. 呈现的语法和语义是同音的
              2. 转换为较低级别的形式(机器代码或解释或其他形式)不会删除任何原始语义

              为什么较低级别的实现在这里很重要?

              还有:

              没有编译时解释器的编译语言

              如果没有某些程序对其进行解释,则它需要是 CPU 的本地语言,因此 CPU 的本地语言需要是同音的(或运行代码的 VM)。

              没有编译时解释的语言......将受到相当大的限制......因为它们根本不会被编译

              但我不是专家,可能没有抓住重点。

              【讨论】:

              • 过程宏是编译时解释的一个例子。
              • 是的,过程宏是编译时评估的一个例子(无论是通过解释还是编译完成都无关紧要)——但是这样的宏可以完全在目标语言之外完成。例如,您可以使用任何预处理器语言作为编译任何其他语言代码的第一步。
              【解决方案9】:

              基于 VM(.net clr、jre ect)构建的语言可以使用允许动态生成代码的高级技术。其中之一是IL编织。虽然,它不像 ECMAScript/Lisp/Scheme 等的 eval 那样清晰,但它可以在一定程度上模拟这种行为。

              查看 Castle DynamicProxy 示例,查看 LinqPAD、F# Interactive、Scala 交互式示例。

              【讨论】:

              • 我不明白这与我的问题有什么关系。
              【解决方案10】:

              是的。 lisp 可以编译为本机二进制文件

              【讨论】:

              • 我没有否认这一点,但二进制文件仍然包含解释器。
              • @ott 和一个 CPU 包含的微码是某种解释器......一切都在某种程度上,一个解释器。
              • @ott,没关系...同音性是某个已定义抽象级别的属性:您可以将同音语言编译到任意级别...并且该级别可能/可能不表达同音语义等...
              • 如果代码被编译,二进制文件不需要解释器。有些 Lisps 甚至没有解释器。
              • @Rainer,甚至没有提到 LISPM / Lisp-on-a-chip 的东西 scribd.com/doc/938809/Design-of-a-LISPBased-Microprocessor
              猜你喜欢
              • 2010-10-02
              • 2010-11-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-02-14
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多