29

根据定义,同音字的意思是:

代码和数据的相同表示

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

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

4

10 回答 10

36

'Homoiconic' 是一种模糊的结构。“代码就是数据”更清晰一些。

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

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

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

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

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

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

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

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

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

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

于 2009-08-06T14:19:28.190 回答
8

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

于 2009-08-06T13:26:23.670 回答
3

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

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

dsm 的机器代码点是一个好点,但提供:

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

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

还:

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

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

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

但我不是专家,也许没有抓住重点。

于 2009-08-06T13:30:58.267 回答
2

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

于 2009-08-06T16:25:38.417 回答
2

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

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

于 2009-08-07T16:44:11.743 回答
2

机器码本身是谐音的,所以是的。

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

于 2009-09-09T22:48:12.660 回答
1

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

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

于 2009-08-06T14:47:08.537 回答
1

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

于 2009-08-07T16:29:28.963 回答
1

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

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

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

于 2012-05-03T21:59:00.840 回答
0

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

例如,请查看 Castle DynamicProxy,更多交互示例请查看 LinqPAD、F# Interactive、Scala 交互。

于 2009-08-06T13:41:59.017 回答