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。
答案 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)
我也在寻找处理同性语言的树(或更好的图表),仍然没有任何积分。让我们列出一些必要的语言元素列表,也许我们会找到一些变体:
答案 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
维度中要深得多。