前一段时间(大约三年),我决定读一本有关Lisp的教科书。 没有任何特定的目的,只是为了总体发展,并且是为了使对话者感到异国情调(一旦看起来,甚至可以解决)。
但是经过仔细检查,Lisp确实非常强大,灵活并且在日常生活中有用。 所有次要的自动化任务都迅速迁移到Lisp中的脚本,并且还有机会实现更复杂的自动化。
这里应该指出的是,“自动化能力”是指这样一种情况,即编写和调试程序的总时间少于手动解决同一任务所花费的时间。
保罗·格雷厄姆(Paul Graham)写了不止一篇文章,甚至写了一本有关Lisp好处的书。 在撰写本文时,Lisp在TOIBE排名中排名第33(死亡人数比死掉的Delphi高三倍)。 问题出现了:如果方便的话,为什么语言这么小? 大约两年的使用说明了一些原因。
缺点
1.共享数据结构这个概念使您可以优化功能程序,但是在命令中却充满了微妙的错误。 如果修改的变量与结构之间没有可见的联系,则可能会意外损坏外部数据结构,这要求程序员不断监视幕后发生的事情,并了解所使用的每个功能(系统和用户)的内部实现。 最令人惊奇的事情是通过修改函数的返回值来破坏函数主体的能力。
2.缺乏封装尽管存在软件包的概念,但它与Ada中的
软件包或Delphi中的
unit无关。 任何代码都可以将任何东西添加到任何包中(系统包除外)。 任何代码都可以使用
::运算符从任何包中提取任何内容。
3. Hapazar的缩写MAPCAN和MAPCON有什么区别? 为什么在SETQ中最后一个字母Q? 鉴于该语言的年代久远,您可以理解这种情况的原因,但是我希望该语言更加简洁。
4.多线程这个缺点与Lisp间接相关,主要与我使用的实现有关-SteelBank Common Lisp。 Common Lisp不支持多线程。 尝试使用SBCL提供的实现失败。
遗憾的是拒绝了这种方便的工具,但不满情绪逐渐累积。
寻找解决方案
首先,您可以在Lisp页面上转到Wikipedia。 检查“方言”部分。 阅读每个的简要介绍。 并意识到所有标记的味道和颜色是不同的。
如果您想做某事,您需要自己做
-Jean Baptiste Emmanuel Sorg
让我们尝试通过添加一些Ada,很多Delphi和一滴Oberon来创建我们自己的正确Lisp。 我们称这种混合物为福克斯。
基本概念
1.没有指针在与PROBLEM-1的斗争中,必须通过复制值来执行所有操作。 根据代码中或打印时数据结构的类型,其所有属性,外部和内部连接都应完全可见。
2.添加模块作为解决问题2的一部分,我们
使用 Ada导入
package 和并
使用语句。 在此过程中,我们丢弃了Lisp符号过于复杂的导入/阴影方案。
(package - ( ) () ())
(with -)
(use -)
3.缩写少最常见和常见的字符仍将被缩写,但最明显的最常见的字符是:
const ,
var 。 格式化输出函数-FMT需要缩减,因为它经常在表达式中找到。
Elt-占领了一个元素-从Common Lisp泄漏并扎根,尽管没有必要减少它。
4.不区分大小写的标识符我认为正确的语言(和文件系统){$ HOLYWAR +}应该不区分大小写{$ HOLYWAR-},以免再次绞尽脑汁。
5.俄语键盘布局易于使用语法Lisi尽可能避免使用其中一种布局中不可用的字符。 没有方括号或花括号。 否#,〜,&,<,>,|。 读取数字文字时,逗号和句点都被视为十进制分隔符。
6.扩展字母SBCL的优点之一是代码中的UTF-8。 声明常量BEAR,VODKA和BALALAYKA的能力大大简化了应用程序代码的编写。 插入Ω,Ψ和Σ的能力使代码中的公式更加直观。 尽管从理论上讲可以使用任何Unicode字符,但是很难保证使用它们的正确性(而不是懒惰而不是困难)。 我们只限于西里尔文,拉丁文和希腊文。
7.数字文字对我来说,这是最有用的语言扩展。
10_000
在我看来,后一种选择最不美观,但最受欢迎。
8.周期Lisp中的循环是非标准的并且非常混乱。 简化到最低标准设置。
(for i 5
循环变量在外部不可见。
(while )
9.转到这不是一个非常必要的运算符,但是没有它,很难证明对结构化编程规则的忽视。
(block : (goto :))
10.范围的统一Lisp中有两种不同的作用域类型:TOPLEVEL和本地。 因此,有两种不同的声明变量的方法。
(defvar A 1) (let ((a 1)) …)
在Fox中,在脚本的顶层和局部区域(包括软件包)都只使用一种方法。
(var A 1)
如果要限制范围,请使用运算符
(block (var A 1) (set A 2) (fmt nil A))
循环的主体包含在隐式BLOCK语句中(类似于函数/过程的主体)。 循环结束时声明的所有变量都在迭代结束时销毁。
11.单槽字符在Lisp中,函数是特殊对象,并存储在特殊符号槽中。 一个字符可以同时存储变量,函数和属性列表。 在狐狸中,每个字符仅具有一个含义。
12.便捷的ELT对Lisp中复杂结构的元素的典型访问如下所示
(elt (slot-value (elt 1) '-2) 3)
Fox实现了一个统一的ELT运算符,该运算符提供对任何复合类型的元素(列表,字符串,记录,字节数组,哈希表)的访问。
(elt 1 \-2 3)
也可以使用Lisp中的宏获得相同的功能
(defmacro field (object &rest f) " . (field *object* 0 :keyword symbol \"string\") . plist. ( ) . ." (if f (symbol-macrolet ((f0 (elt f 0))(rest (subseq f 1))) (cond ((numberp f0) `(field (elt ,object ,f0) ,@rest)) ((keywordp f0) `(field (getf ,object ,f0) ,@rest)) ((stringp f0) `(field (cdr (assoc ,f0 ,object :test 'equal)) ,@rest)) ((and (listp f0) (= 2 (length f0))) `(field (,(car f0) ,(cadr f0) ,object) ,@rest)) ((symbolp f0) `(field (,f0 ,object) ,@rest)) (t `(error " ")))) object))
13.子程序参数传输模式的限制Lisp中至少有五种参数传递模式:强制性,
&可选 ,
&rest ,
&key 和&整体,并允许其任意组合。 实际上,大多数组合都会产生奇怪的效果。
在Fox中,仅允许使用必需参数和以下模式之一的组合,以从以下各项中进行选择
:键 ,:可选,: 标志 ,:其余 。
14.多线程为了最大程度地简化多线程程序的编写,采用了内存分离的概念。 生成线程时,将复制新线程可用的所有变量。 对这些变量的所有引用均由对副本的引用替换。 流之间的信息传递只有通过受保护的对象或流完成时返回的结果才有可能。
受保护的对象始终包含关键部分以确保原子操作。 登录关键部分是自动的-语言没有单独的操作符。 受保护的对象包括:消息队列,控制台和文件描述符。
使用多线程显示功能可以创建线程
(map-th (function (x) …) --)
Map-th自动启动等于系统中处理器数量的线程数(如果内部装有Intel,则为两倍)。 在递归调用中,后续的第map个调用在单个线程中工作。
此外,还有一个内置的线程函数可以在单独的线程中执行过程/函数。
15.命令代码中的功能清洁Fox具有用于函数式编程和程序过程的功能。 使用function关键字声明的例程必须满足以下要求:没有副作用,并且结果不受外界因素影响。
未实现
由于优先级较低,Lisp的一些有趣功能仍未实现。
1.广义方法能够使用defgeneric / defmethod重载函数。
2.继承3.内置调试器发生异常时,Lisp解释器将切换到调试模式。
4.超滤用于连接以其他语言编写的模块的接口。
5.大数字任意位深度支持
舍弃Lisp的某些功能已被考虑为无用/有害。
1.方法的指导性结合当为类调用方法时,将执行父方法的组合,并且可以更改组合规则。 该方法的最终行为似乎难以预测。
2.重新启动异常处理程序可以更改程序的状态,并将重新启动命令发送到生成异常的代码。 该应用程序的效果类似于使用GOTO运算符从一个功能切换到另一个功能。
3.罗马帐Lisp支持数字系统,该数字系统在出现之前不久已经过时。
使用方法
这是一些简单的代码示例。
(function crc8 (data :optional seed) (var result (if-nil seed 0)) (var s_data data) (for bit 8 (if (= (bit-and (bit-xor result s_data) $01) 0) (set result (shift result -1 8)) (else (set result (bit-xor result $18)) (set result (shift result -1 8)) (set result (bit-or result $80)))) (set s_data (shift s_data -1 8))) result)
实作
解释器是用Delphi(兼容模式下的FreePascal)编写的。 它是在Windows和Linux 32位和64位操作系统下的Lazarus 1.6.2和更高版本中构建的。 在外部依赖项中,需要libmysql.dll。 包含约15_000..20_000行。 大约有200种内置函数可用于各种用途(其中一些过载八次)。
存放在这里对动态类型的支持以简单的方式执行-所有处理的数据类型均由相同TValue类的继承人表示。
对于Lisp来说,最重要的类型-列表是Delphi惯用的,它是一个包含TValue类型的动态对象数组的类。 对于这种类型,将实现CopyOnWrite机制。
内存管理是基于引用计数自动进行的。 对于递归结构,将同时计算结构中的所有链接。 当变量退出作用域时,内存释放立即开始。 没有用于延迟启动垃圾收集器的机制。
异常处理基于Delphi中内置的机制。 因此,可以通过Fox上的可执行代码来处理解释器代码中发生的错误。
每个运算符或内置Lisi函数都作为解释器代码中的方法或函数实现。 该脚本是通过实现的相互递归调用来执行的。 解释器代码和脚本具有公共调用堆栈。
脚本变量独立存储在动态内存中。 每个用户定义的函数都有自己的堆栈,用于存储变量引用,而与顶级堆栈或父函数的堆栈无关。
特别困难的是结构元素的赋值运算符(集合)的实现。 直接计算指向所需元素的指针会导致悬挂链接的风险,因为Lisi语法不会在计算所需元素时禁止修改结构。 作为一种折衷解决方案,实现了“链指针”-一个对象,该对象包含对变量的引用和数字索引数组,以指示结构内的路径。 这样的指针也容易出现悬空链接的问题,但是在失败的情况下,它会生成有意义的错误消息。
开发工具
1.控制台2.文字编辑器配备了语法突出显示功能以及在F9中运行可编辑脚本的功能。

结论
在当前状态下,该项目解决了其设想的问题,并且不需要进一步的积极开发。 当前存在的许多缺陷对工作没有明显影响。