小智 5
我应该拒绝添加答案,但我不能。正如我在评论中所说:语言是标准组织的方言——没有真正有用的技术定义。话虽如此,这里有一些我认为 Lisp 应该是 Lisp 的属性。注意我不是说“Lisp 的方言”。
if&其他条件的语法)和函数调用之间不应该有很大的语法区别。原因是宏:在存在宏系统的情况下,语法的含义是由程序员定义的,而在语言的语法中拥有特殊特权的东西是它的敌人。这也意味着宏所处理的解析树应该相当通用,并且不附加任何语义。lambda。 它不必被调用lambda(可以是fn或其他任何东西),但不应该以任何方式限制它。特别是应该清楚的是,函数定义可以根据lambda一些宏观学来完成。(a . b)cons 也是 如此,(a b c)与 相同(a . (b . (c . ())))。注释和列表不必这样写,但符号应该很简单,如果它们是简单的,可能会有所帮助。特别是列表应该是单链接的,而不是经过修饰的数组。我认为这些并不是绝对必要的,但有帮助。
这些根本不是要求,但我不会认真使用没有这些功能的 Lisp。
有一些评论认为应该放弃其中一些要求。
大约在 1963 年之前,Lisp 没有宏:关于 Lisp 的原始论文没有提及它们,例如:我想,第一次讨论它们的论文就在这里。因此,在 1963 年之前,根据我的定义,称为 LISP 的语言并不是 Lisp。根据这里给出的定义,允许这种语言成为 Lisp 的唯一方法就是说,今天要成为 Lisp,你不需要宏系统,我认为这显然是荒谬的。
因此,您要么需要接受 Lisp 的定义随着时间的推移而发生变化,1963 年之前的 Lisp 可能是“旧 Lisp”,要么您可以说它是Lisp 的一部分。两者都可以:删除宏则不然。
人们很容易会说——尤其是如果你想让 Python 成为 Lisp 的话——整个缺点都是转移注意力的:带有可扩展数组的列表的语言可以是 Lisp。这也是错误的,因为用列表为可扩展数组的语言编写的程序与用列表为cons链的语言编写的程序完全不同。
考虑这个任务:给定一个列表(可能是一个长列表),从中随机选择 n 个元素。这是一个简单的 CL 函数来执行此操作:
(defun pick-n-random-elements (n l)
(loop repeat n
collect (nth (random (length l)) l)))
Run Code Online (Sandbox Code Playgroud)
这个函数在Lisp 中很糟糕:它在每次迭代时都会调用lengthand nth。您可以将这种函数作为示例提供给学生,看看他们是否理解列表在 Lisp 中的工作原理。
但在列表是可扩展数组的语言中,这个函数完全没问题:等效的 Python 函数
from random import randint
def pick_n_random_elements(n, l):
r = []
for i in range(n):
r.append(l[randint(0, len(l) - 1)])
return r
Run Code Online (Sandbox Code Playgroud)
虽然不是很惯用的 Python,但它并没有潜伏着同样的性能恐怖。(我认为惯用的Python可能是
from random import choice
def pick_n_random_elements(n, l):
return [choice(l) for i in range(n)]
Run Code Online (Sandbox Code Playgroud)
虽然我不太确定。)
要在 Lisp 中执行此操作,您可以编写如下内容:
(defun pick-n-random-elements (n l)
(labels ((pick (n tail elts accum)
(cond ((null elts)
accum)
((= (first elts) n)
(pick n tail (rest elts) (cons (first tail) accum)))
(t
(pick (1+ n) (rest tail) elts accum)))))
(pick 0 l (sort (loop with len = (length l)
repeat n
collect (random len))
#'<)
'())))
Run Code Online (Sandbox Code Playgroud)
请注意,这将返回与前一个函数为给定随机种子返回的序列相反的序列:假设您实际上想要随机元素,这并不重要。还要注意,它依赖于尾部调用消除的效率,虽然非常自然的 Lisp 风格,但你不能完全在 CL 中假设——这是一个我认为既符合 Lisp 习惯又不依赖于此的版本:
(defun pick-n-random-elements (n l)
(let* ((len (length l))
(indices (sort (collecting (repeat n (collect (random len))))
#'<))
(tail l)
(i 0))
(collecting
(while (not (null indices))
(while (and (not (null indices))
(= (first indices) i))
(collect (first tail))
(setf indices (rest indices)))
(setf tail (rest tail)
i (1+ i))))))
Run Code Online (Sandbox Code Playgroud)
当然,这使用了三个不同的非标准宏:repeat、while和collecting/ collect(前两个是微不足道的,最后一个是我想每个人都在某个时候写过的东西),从而很好地表明没有宏系统的语言也不是 Lisp。
这些例子应该让您相信,虽然具有可扩展数组列表的语言表面上看起来像 Lisp,但您用它编写的程序与您使用具有由 cons 组成的列表的语言编写的程序有很大不同:如果你尝试将这些程序转换为 Lisp,它们通常会出现灾难性的性能问题。反之亦然:用 Lisp 编写程序很容易,而用可扩展数组语言编写程序会导致重复复制大型数组,从而带来严重的性能问题。
我在这里试图做的是集中精力于重要的事情:很容易说表面上“看起来”像 Lisp 的东西就是 Lisp,或者说与 Lisp 有某种家族相似性的语言就是 Lisp:Norvig for实例有句名言
Python 可以被视为具有“传统”语法的 Lisp 方言(Lisp 人们称之为“infix”或“m-lisp”语法)。
我相信他错了:Python 不是一种表达式语言,没有conses,没有宏系统,有一个残缺的lambda,没有符号类型,并且实际上没有最小承诺的语法。它不是 Lisp。
相反,我尝试关注用 Lisp 编写程序的感觉(以及今天而不是 1960 年用 Lisp 编写程序的感觉),因为我认为这才是重要的。我认为我对 Lisp 的强烈要求对于用 Lisp 编程的感觉至关重要:如果其中任何一个不适用于一种语言,那么用 Lisp 编程与用 Lisp 编程是一种非常不同的体验。
当然,这只是我的观点,尽管我的观点是基于 30 多年的 Lisp 编写(我刚刚意识到这一点:我不确定我是否应该为此感到沮丧)。