【问题标题】:Is my definition of the LISP cons-cell list correct?我对 LISP cons-cell 列表的定义是否正确?
【发布时间】:2018-01-08 15:26:47
【问题描述】:

我正在努力真正了解 LISP,以便为前进打下良好的基础,但进展缓慢,因为 LISP(在我的案例中特别是 Common Lisp)不遵循任何 C 系列命名约定。

这是我对 LISP 列表的个人定义,基本上正确吗?: LISP 中的所有列表都构造为单链表,对节点和下一个指针都使用 void 指针。

编辑:为澄清起见,我使用“空指针”一词来表示关于 cons-cell 的 CAR 和 CDR 的性质的想法。我知道 LISP 中不存在“空指针”,我正在尝试将 concepts 从 C 应用到 LISP

【问题讨论】:

  • " 基本正确吗?:" - 不,你为什么要使用 void 指针?为什么它们必须用 C 或 C++ 实现?
  • 我正在尝试将我对 C 语言家族的计算理解应用到外来的理解中,在本例中为 Lisp。重要的不是 LISP 上下文中的名称“空指针”,而是“空指针”意味着指向我大脑的无类型指针。我在问这种理解是否正确
  • LISP 不遵循 C 约定,因为它已经老了十年...最好不要尝试将 LISP 概念映射到 C,因为它们是非常不同的语言...
  • C 对链表没有任何约定。标准 C 库中没有链表。每个程序员都为链表节点选择自己的名称,以及它的表示形式:它是双向链接的,还是单向链接的;它是否包含数据或指向它,等等。实现 Lisp 方言的 C 程序通常使用 Lisp 术语,例如 struct cons { value *car, *cdr; }

标签: lisp common-lisp


【解决方案1】:

基本的 Lisp 数据结构,用 C 语言术语表示,可能如下所示:

/* A value is a "discriminated union". */
typedef struct value {
  /* It has a type field. */
  enum type { t_cons, t_symbol, t_fixnum, t_character /* , ... */ } type;

  /* And then one of several payloads, overlaid in the same space,
     which one being there depending on the type field. */
  union {
    struct symbol *sym;
    struct cons *cons;
    int fixnum;   /* unboxed integer: no heap allocation */
    /* ... */
  } u;
} value;

/* This is the heap-allocated part of a cons cell;
   not the complete cons cell value, which is actually
   of "struct value" type. See cons()
   function below which makes a cons value. */
struct cons {
  struct value car, cdr;
};

static  value nil = { t_symbol };

value cons(value a, value d)
{
   value retv;
   struct cons *c = allocate_cons(); /* from special cons heap */
   c->car = a;
   c->cdr = c;
   retv.type = t_cons;
   retv.u.cons = c;
   return retv;
}

int is_nil(value v)
{
  return (v.type == t_symbol && v.sym == NULL);
}

value cons(value a, value d)
{
   struct value retv;
   struct cons *c = allocate_cons(); /* from special cons heap */
   c->car = a;
   c->cdr = c;
   retv.type = t_cons;
   retv.u.cons = c;
   return retv;
}

value car(value arg)
{
  switch (arg.type) {
  case t_cons:
    return arg.u.cons->car;
  case t_symbol:
    if (is_nil(arg))   /* (car nil) -> nil */
      return nil;
    /* fallthrough */
  default:
    /* This function generates a Lisp exception somehow */
    throw_error("car: not applicable to ~s", arg);
  }
}

Lisp 概念没有详细说明数据结构。

实际的 Lisp 实现通常会做一些更聪明的事情来更紧凑地表示 value。一种常见的技术是使用机器字(现在通常是指针大小)作为 Lisp 值,并在该字中使用几个 标记位 来指示它是否是指向堆上某物的指针,或者整数的直接表示。 (这意味着 Lisp fixnum 整数不使用所有可用的 32 或 64 位,但可能只有 30 或 62。更大的整数是不同的类型 bignum,并且是堆分配的。)

然而,将结构体用于值而不是指针,为浮点值创造了按值语义的机会,这对数字代码来说是一个胜利。这意味着浮点对象不必在堆中分配,而是存储在一个值中。

用 C 编写的 Lisp 实现可以做这种诡计,但它会导致 ISO C 未定义的行为,并且声明和代码对于说明目的来说并不那么好。

对于这种类型的表示,一个很好的细节是为 Lisp 符号 nil 使用 C 空指针。然后,任何用 C 编写的内部例程都可以使用与 Lisp 相同的约定以符合人体工程学的方式编写:nil 既是假的又是空列表。

C 受 Lisp 的影响很大,因为它基于返回值的表达式,并且空指针是错误的。 C 中的 a?b:c 三元运算符在某种程度上是对 Lisp 的 (if a b c) 的仿冒。

要引导一些看起来像 Lisp 语义的东西需要相当多的 C 代码行,并且它的许多方面都有多种设计选择。因此,最好尝试将 Lisp 理解为一种抽象,而不是通过特定详细的数据结构和执行模型设计,更不用说用 C 来表达了。

【讨论】:

    【解决方案2】:

    Common Lisp HyperSpec 是标准,它描述了表单和函数的行为以及可能对复杂性的一些要求,但它从不干扰如何完成数据结构的低级实现。您可以使用不公开任何硬件的语言来实现 CL,也可以使用 void* 指针作为值来实现 C。

    在许多实现中,尽管值是机器中的指针,因此 void* 完美地描述了它。 Cons 是两个机器字的数组,carcdr 可以在其中包含任何值。因此,除非您将其限制在您的使用范围内,否则它不仅仅是一个简单的链接列表。您可以使用它制作树结构或建模任何类型的 og 数据结构。

    由于所有指针都是字节的索引,并且一个字通常是 4 或 8 个字节,因此每个指针将有 2 或 3 位始终为零。许多实现巧妙地在这些位中添加信息,以告知指针代表什么。例如。如果 lsb 为 1,则可能会将地址解释为固定的有符号整数,并且您可以通过使用进位将其右移 1 位来获得它的实际值。一个 cons 单元可能将所有标记位设置为2。这就是为什么两个固定编号可以是eq

    现在一个正确的列表(1 2 3) 被读入并像这样打印,但是如果你读到(1 . (2 . (3 . ()))) 你会得到相同的输出。使用cons 的列表错觉嵌入在readwrite 函数中,因此它仅在cdr 具有正确类型时才起作用。 cons 可以有任何值,但如果cdr 不是consnil,则会以虚线表示。

    【讨论】:

      【解决方案3】:

      将 Lisp 列表视为在 CAR 中具有值的 CONS 对和在 CDR 中具有列表或 nil(空列表)的列表会有所帮助。您可以将任何列表视为其结构上的缺点。

      例如(list 1 2 3) => (cons 1 (cons 2 (cons 3 nil)))

      【讨论】:

        【解决方案4】:

        希望Common Lisp Cookbook 上的新数据结构页面中的解释适合您的观点。

        (简而言之,我不是专业人士,但我倾向于同意您的定义:列表是一个以空指针结尾的 cons 单元的延续)。

        列表基本元素是 cons 单元格。我们通过组装建立列表 缺点细胞。

        (cons 1 2)
        ;; => (1 . 2) ;; representation with a point, a dotted pair.
        

        看起来像这样:

        [o|o]--- 2
         |
         1
        

        如果第一个单元格的cdrrest)是另一个cons单元格,如果cdr的 最后一个是nil,我们建立一个列表:

        (cons 1 (cons 2 nil))
        ;; => (1 2)
        

        看起来像这样:

        [o|o]---[o|/]
         |       |
         1       2
        

        draw-cons-tree 的 ascii 艺术)。

        看到表示不是点对吗? Lisp 打印机 理解约定。

        最后我们可以简单地用list构建一个文字列表:

        (list 1 2)
        ;; => (1 2)
        

        或通过调用报价:

        '(1 2)
        ;; => (1 2)
        

        这是特殊形式(quote (1 2))的简写符号。

        【讨论】:

          猜你喜欢
          • 2010-11-21
          • 1970-01-01
          • 1970-01-01
          • 2021-12-27
          • 1970-01-01
          • 2010-09-28
          • 2013-05-12
          • 1970-01-01
          • 2016-06-28
          相关资源
          最近更新 更多