作者:fengzi199171 | 来源:互联网 | 2023-02-05 23:07
我尝试使用递归在Clojure中创建阶乘函数
(defn fac[x] (if (= x 1) 1 (* x (fac (- x 1)))))
现在,当我尝试调用该函数时
(fac 5)
我得到了例外
java.lang.IllegalStateException: Attempting to call unbound fn: #'sandbox11355/fac
这是否意味着在使用defn
关键字定义函数时不可能使用递归?
另外,我如何最有效地接受这种功能语法,因为我已经习惯了命令式/ OOP思维方式?以相反的顺序键入所有内容感觉很尴尬.通过程序范式,思想的连续体直接映射到改变价值的新代码行.使用函数语法,对于操作当前值的每个步骤,我必须围绕表达式包装新函数,并且很难跟踪parens和范围.我是否应该学会以相反的顺序思考程序模型以流利地编写功能样式?
我理解没有可变状态和纯函数(更少的错误)的好处,但很难相信它失去编写过程代码的便利性.就目前而言,所有这些似乎都过度炒作无组织的混乱,但也许它开始有意义.
1> Josh..:
关于您对功能和程序编程的关注的一些信息如下.它不是特别原创,但它可能会让你开始思考如何考虑这些新东西.
函数式编程不是反过来的过程式编程.它是一个更高级别的抽象,我们与之交互的大多数东西都可以看作是一种抽象; 否则,我们永远不会得到任何有用的东西,因为我们会如此关注我们处理的每件小事的细节.同样,任何语言的所有代码最终都成为CPU的一系列指令,这些指令是"命令"或"程序"的缩影.问题变成了,"为了解决我的问题,我需要对极低的细节进行多少控制?"
将一些数字加在一起的一种方法,非常明确(只是伪代码,希望意图很清楚):
int nums[10] = {0,1,2,3,4,5,6,7,8,9};
int i = 0;
int acc = 0;
start_loop:
if (i >= 10) goto done_loop;
int num_address = (nums + i);
int num_value = *num_address;
acc = acc + num_value;
i = i + 1;
goto start_loop;
done_loop:
return acc;
这很乏味,但不像汇编代码那样乏味.为了抽象出循环的一些细节,C/java/etc提供了一个称为for
循环的控制结构:
int nums[10] = {0,1,2,3,4,5,6,7,8,9};
int acc = 0;
for (int i = 0; i <10; i++)
acc += nums[i];
return acc;
当然,当您定期编写命令式代码时,这似乎是完全正常的.您可以迭代地思考,以及如何在每个偏移量的基础上访问数组的细节.然而,这也可以被认为是乏味的.我为什么要关心如何访问数组的每个成员的细节?调用任何函数式语言提供的进一步抽象reduce
.可以将其视为以与for
C/java/etc程序员相似的方式提供的工具.它看起来很奇怪,就像for
汇编程序员第一次看到它的语法一样:
(reduce + (range 10))
所以我们在这里所做的只是抽象出循环的细节,以至于我们真的不太关注实际发生的循环.我们还抽象出了创建一个明确的数字范围的细节,并且只是说"给我从0(包括)到10(不包括)的整数".它只是抽象出细节.结果通常是能够更多地关注手头的问题.
为了增加数字或思考更高级别,一种功能性的编程方式通常允许我们以更少的代码提高工作效率,同时让各级编译器为我们处理混乱的细节.但是,如果问题的水平很低,那么我们可能希望语言中的结构更适合我们的问题.关键是始终使用正确的工具来完成正确的工作.
当然,它不是一个完美的世界,而且经常在clojure中我们被迫编写处理位和字节的低级细节,同步并发代码,循环等等.但是总体来说,是声明,并说明是什么你想做的事,而不是更加明确如何做到这一点,有很多好处.
做4clojure问题,给它一两个月开始真正有意义,并允许你的思想从变异变量转变为评估表达式.您很有可能会非常享受它,而最糟糕的情况是您可以开阔视野.祝好运并玩得开心点!