Lisp用于列表处理。树处理有语言吗?

时间:2010-09-17 22:06:11

标签: list tree lisp language-design parallel-processing

Lisp的名称源自 LIS t P 处理。链表是Lisp语言的主要数据结构,Lisp源代码本身由列表组成。因此,Lisp程序可以将源代码作为数据结构进行操作(这称为同音性)。

但是,根据定义,列表是顺序构造。这鼓励我们使用顺序语言习语(一次处理一件事并累积结果的算法)来解决问题。例如,在Lisp中, cons 单元用于实现单链表, car 操作返回列表的第一个元素,而 cdr 返回列表的其余部分。我的愿景是并行执行的函数式语言,它将问题分解为大致相等的子问题,递归地解决它们,并结合子解决方案。

几乎每种编程语言的代码都已解析为树;是否有像Lisp这样的同性语言,但树木是主要的数据结构?顺便说一句,我称之为Treep,因为 TREE P rocessing。

更新: 2009年由Guy Steele撰写的关于并行算法的有趣演示文稿(PDF)数据结构,Organizing Functional Code for Parallel Execution: or, foldl and foldr Considered Slightly Harmful

8 个答案:

答案 0 :(得分:6)

我不认为这种变化会非常深刻。 Lisp肯定没有任何问题让列表成为其他列表的成员,因此它可以很容易地表示树和树上的算法。

相反,每个列表都可以被视为特定形状的树(以各种方式)。

答案 1 :(得分:6)

Lisp列表ARE树,Lisp代码是树,就像任何其他代码一样。

(+(* 1 3)(* 4 6))

是一棵树:

     +
    / \
   /   \
   *   *
  / \ / \
  1 3 4 6

而且它不只是二叉树。

(+ 1 2 3)

   +
  /|\
 / | \
1  2  3

所以,也许Lisp是你的答案以及你的问题。

答案 2 :(得分:1)

我想说Lisp语言的主要数据结构是 cons cell 。使用cons单元可以轻松构建的一个东西是链表,但这绝不是唯一的数据结构。

cons单元格是一对数据项,但没有任何内容表示值必须位于左侧单元格中,而指针位于右侧单元格中(如链接列表中)。如果允许两个单元格本身包含值或指针,则可以轻松构建二进制(或使用更多工作,n-ary)树结构。基于这些结构,人们可以构建字典或B树或您可能想到的任何其他数据结构。

答案 3 :(得分:1)

OP似乎对处理树有一些并行支持的语言感兴趣。

我们的DMS Software Reengineering Toolkit是一个通用的程序分析和转换引擎。它将编程语言源文本解析为树(OP的第一个兴趣),支持分析和处理这些树,并从这些树中重新生成源文本。 DMS由描述正在处理的语言的显式语法驱动,并且它具有许多适用于它的生产质量语言定义。

DMS的树处理机制为两个不同级别的并行提供支持,一个由DMS的底层并行编程语言PARLANSE直接支持,它受LISP的启发但不是那么动态。

PARLANSE提供称为“谷物”的平行活动的“团队”(如在沙滩上的沙子,想法是有很多谷物)。团队可以动态构建,通过(经典地)分配新的谷物并将它们添加到(动态)团队;这很有用,但速度并不快。团队可以静态构建,包括“将这个固定大小的粒子组合为纯粹的平行”:

(|| a b c)

和“创建一组谷物,其执行顺序由指定的偏序”(!| ...)控制。您可以直接在fork调用中编写部分订单:

(!| step1 a
    step2 b
    step3 (>> step1 step2) c
    step4 (>> step2) d )

编码行为 c 在(稍后>> 及时)) a b之后发生的事实完成。由PARLANSE编译器预先编译静态编译的团队到非常有效的结构,因为谷物可以非常快速地发射和杀死,允许非常小的粒度(几百个指令)。标准并行库的开销大大高于此。

处理树的基本方法非常传统:有一个PARLANSE工具库,用于检查树节点,在树上走来走去,创建新节点并将它们拼接到位。递归过程通常用于访问/更新树。

这里的关键观察是,访问某些子集的树访问可以按顺序编码,或者可以轻松编码为静态并行团队。因此,手动编写并行树访问代码非常容易,代价是为每个树节点类型编写许多特殊情况并行fork。 OP的兴趣似乎是“分而治之”。 (你当然可以枚举一个节点'childern,并使用一个动态团队为每个孩子分叉谷物,但这不是那么快)。这是用于在DMS中处理树的第一级并行度。

第二级并行性来自DMS提供的DSL,它实现了属性语法(AG)。

AG是用一组计算装饰BNF的函数式语言。可以用DMS中的AG编写一个简单的计算器:

   sum = sum + product;
        <<Calculator>>:  sum[0].result = sum[1].result + product.result;

这会导致通过组合第一个子节点(sum [1])和第二个子节点(product.result)的结果属性为根(sum [0])计算属性“result”。 所谓的合成属性从叶子向上传播信息;继承属性从父母传播信息。一般的属性语法和DMS的AG允许混合它们,因此信息可以以任意顺序在树中上下流动。

大多数AG是纯功能性的,例如没有副作用; DMS可以产生副作用,使符号复杂化,但在实践中非常有用。一个很好的例子是通过属性评估构造符号表; current-scope在树中传递,local-declaration块创建新的当前作用域并将其传递下来,并且各个声明将符号表数据存储到从父接收的符号表条目中。

DMS的AG编译器分析属性计算,确定信息如何在整个树中流动,然后生成并行PARLANSE程序以实现每个树节点类型的信息流。它可以对每个AG规则进行数据流分析,以确定信息流以及首先发生的事件与之后的比较。对于上面的简单求和规则,应该清楚的是,在计算root的属性之前必须计算子属性。事实证明,PARLANSE的部分顺序构造是对这些信息进行编码的完美方式,甚至可以很好地处理我们添加到AG的副作用。

结果是DMS将AG规范编译成高度并行的PARLANSE程序。我们的C ++前端名称/类型解析器实现为大约150K行的DMS AG(是的,它描述了如何使用C ++类型信息需要很多),它编译 到并行PARLANSE代码的700K SLOC。并且它工作(并且在x86多核机器中并行运行)没有任何想法或调试,它似乎可以很好地扩展。

答案 4 :(得分:0)

同源树处理语言的规范示例是XSLT。但是你可能不想写(或读)任何实质内容。

答案 5 :(得分:0)

我认为闭包有树而不是列表,并且在出于并发目的时使用它们。

答案 6 :(得分:0)

我也在寻找处理同性语言的树(或更好的图表),仍然没有任何积分。让我们列出一些必要的语言元素列表,也许我们会找到一些变体:

  • 同质性:基于树(或更好的图形)解释
  • 语言核心中的属性语法支持
  • 与统一的结构模式匹配
  • 全套典型图算法
  • 具有丰富可视化支持的类似SmallTalk的交互式环境
  • 用于任何文本数据加载的语言核心的词法分析器/解析器引擎
  • 嵌入式(基于LLVM)编译器框架,用于低级代码生成和密集计算任务的优化

答案 7 :(得分:0)

“Lisp”只是一个名字和一个古老的名字。它没有描述在Lisp中完成的所有操作。就像Fortran不仅仅是一个公式翻译,而且Cobol中的所有内容都不是“面向商业”。

“Lisp”中引用的处理实际上是树处理,因为它是嵌套列表的处理。它是嵌套列表处理。只是“嵌套”没有包含在名称中。

术语表中的ANSI Common Lisp标准normatively defines "tree" and "tree structure"

Lisp列表是树结构中的约定:右倾递归(通过cons单元的cdr槽)代表一个列表。有一个符号可以使用它:即树符号结构((((a . b) . c) . d) . nil)可以缩短为(a b c d),根据编写为(anything . nil)的利弊单元也可以打印为(anything)的符号规则{1}}以及(foo . (bar))可以写成(foo bar)的规则。

可能强调术语“列表处理”并不是因为树被排除,而是因为树中的大部分增长处于被理解为水平的方向:也就是说,处理嵌套列表< / strong>即可。

在典型的Lisp程序中处理的大多数基于列表的数据结构,即使是嵌套的,在cdr维度上比在car维度中要深得多。