除非您更有经验,否则我建议您学习在 Lisp 中编写 Lisp,而不是如何在 Lisp 中编写 Haskell。后者不是一个好主意。Haskell 的工作方式非常不同。
Lisp 不做任何“currying”(或 schönfinkeling ;-))。
你可以把它写成:
CL-USER 5 > (defun curry (fn arg) (lambda (&rest args) (apply fn arg args)))
CURRY
CL-USER 6 > (mapcar (curry #'expt 2) '(2 3 4 5 6))
(4 8 16 32 64)
不过,这样会花费一点效率。
CL-USER 7 > (mapcar (lambda (base) (expt base 2)) '(2 3 4 5 6))
(4 8 16 32 64)
我个人更喜欢后者,因为我有一个真正可读的变量名称。这有助于调试器,在那里我看到了回溯。像这样的工具在 Lisp 中可能比在 Haskell 中更重要。
CL-USER 12 > (mapcar (lambda (base) (expt base 2)) '(2 3 "four" 5 6))
错误。让我们看看回溯:
CL-USER 12 : 1 > :bb
...
Condition: In EXPT of ("four" 2) arguments should be of type NUMBER.
Call to SYSTEM::ARGS-TO-BINARY-ARITHMETIC-FN-NOT-OF-TYPE {offset 189}
SYSTEM::FN-NAME : EXPT
SYSTEM::ARG1 : "four"
SYSTEM::ARG2 : 2
TYPE {Closing} : NUMBER
Interpreted call to (SUBFUNCTION :ANONYMOUS SYSTEM::ANONYMOUS-LAMBDA):
BASE : "four"
现在我可以看到这个东西有一个名字。我将字符串传递"four"
给带有名为base
.
使用 REPL 和调试工具进行交互式开发很常见。最好准备对这种开发风格有用的代码。Common Lisp 没有经过优化,无法为完整的程序编译器提供广泛的类型检查——就像在 Haskell 中一样。
Lisp的主要问题之一是很难找出一段代码的真正作用。默认(带有前缀语法的严格函数式程序)相对容易理解。但是有很多可能性可以改变 Lisp 中代码的含义(宏、读取宏、符号宏、元对象协议、建议......)。
第一条规则:如果您正在编写基本的 Lisp 代码,请坚持使用基本的句法和语义可能性。防御性地写。期望其他人需要理解代码。为此,代码应该是可读的、易于理解的、使用常见的成语并且应该是可调试的。
在 Haskell 中,许多具有数学背景的人希望以非常紧凑的方式编写具有高度抽象性的代码。你也可以在 Lisp 中做到这一点。但是对于普通代码,我不会走这条路,对于较大的代码,Lisp 经常使用其他机制(通过宏进行代码转换,...)。