作为函数式语言的新手(几周前我开始接触Erlang - 第一种可以让我动手的功能语言)。
我开始编写一些小算法(例如left_rotate_list
,bubble_sort,
merge_sort
等。我发现自己常常迷失在诸如“我应该使用帮助列表进行中间结果存储吗?”等决策中。并且“我应该创建一个辅助函数来执行此操作吗?”
过了一会儿,我发现函数式编程(如果我说的话没有意义的话,请耐心等待)鼓励“自上而下”的设计:即,当我做merge_sort时,你首先记下所有的合并对步骤进行排序,并将它们命名为单独的辅助函数;然后逐个实现这些辅助函数(如果需要进一步划分这些辅助函数,请以相同的方法执行)。
这似乎与OO设计略有矛盾,您可以从底部开始构建基本数据结构,然后将数据结构和算法组合成您想要的。
感谢您的评论。是的,我希望得到关于如何“用函数式语言思考”的建议(就像“用Java思考”,“用C ++思考”)。
答案 0 :(得分:10)
答案是函数式编程是使用函数进行编程,因为它们是在数学中定义的(简而言之,是将值从域映射到codomain的无副作用的东西)。实际上将其转化为“如何思考”是一个难以详尽解释的挥手部分,但我将对我的一些想法进行抽样:
第一名与模糊相关:“优雅代码”。列表推导可以呈现非常简洁和数学方程式,如函数定义。只需看看LC实现的快速排序。这就是我如何定义优雅,简洁并使所有行为清晰。不是perl code-golf,你最常用的是简洁和神秘。
第二号是我在所有编程中日常使用的东西。将代码划分为当前状态的函数(方法,例程等),这些函数是无副作用的计算,为下一个要采取的操作提供输入(即使是下一个要采取的操作)。返回值时,将其提供给执行所描述操作的例程,然后重新开始。
在我的脑海中,我将Erlang过程描绘为一个状态机图,其中每个顶点是一个副作用和一个函数,其输出是从顶点中选择的边。对副作用的高度重视是函数式编程范式教给我的东西。特别是在Erlang中,因为副作用在并发性方面确实很重要,而且Erlang使得并发性非常可用。
同样地,一些孤立的部落对于3以上的数字只有一个单词,或者对于“我的”/“你的”没有单词。感觉流行的语言没有“这将导致副作用”的词,但功能编程有它。它迫使你一直意识到这一点,这是一件好事。
答案 1 :(得分:5)
过了一会儿,我发现函数式编程鼓励了“自上而下”的设计。
嗯,这不是关于“自上而下”或“自下而上”的设计。这是关注手头问题的“内容”,而不是“如何”。当我开始使用函数式编程时,我发现我一直在回想起C中嵌套的for
循环等命令式构造。然后我很快发现尝试将我的命令式思维转换为函数式构造非常困难。我会试着给你一个更具体的例子。我将在C和Haskell中实现一个等效的程序,并尝试在两种情况下跟踪我的思维过程。请注意,出于解释的目的,我已经明确地详细说明了。
在C:
#include <stdio.h>
int main(void)
{
int i, inputNumber, primeFlag = 1;
scanf("%d", &inputNumber);
for(i = 2; i <= inputNumber / 2; i ++)
{
if (inputNumber % i == 0)
{
primeFlag = 0;
break;
}
}
if (primeFlag == 0) printf("False\n");
else printf ("True\n");
return 0;
}
跟踪我的思考过程:
inputNumber
。 scanf()写的。primeFlag
已声明并设置为1
。primeNumber
的每个号码检查primeNumber/2
。 for
循环开始了。声明了一个循环变量i
来检查primeNumber
。primeNumber
检查i
。我们找到一个i
除以primeNumber
的时刻,将primeFlag
设置为0
和break
。循环体写。for
循环中完成严格的检查过程后,请检查primeFlag
的值并将其报告给用户。 printf()写的。在Haskell:
assertPrime :: (Integral a) => a -> Bool
assertPrime x = null divisors
where divisors = takeWhile (<= div x 2) [y | y <- [2..], mod x y == 0]
跟踪我的思考过程:
null divisors
。divisors
?首先,让我们写下可能的候选人名单。写下德克萨斯范围从2到数/ 2。mod x y == 0
我想得到关于如何做的建议 “用功能语言思考”
好的,首先,想想“什么”,而不是“怎么样”。这需要很多练习才能习惯。另外,如果您以前是像我这样的C / C ++程序员,请不要担心内存!现代语言有一个垃圾收集器,它是为你编写的 - 所以甚至不要尝试修改变量。另一件事对我有所帮助:在你的程序中写下类似英语的定义,以抽象出那些繁重的函数。
答案 2 :(得分:4)
过了一会儿,我发现函数式编程鼓励了“自上而下”的设计。
我不确定这是一个准确的陈述。我最近一直在尝试自学函数编程,我发现一种“自下而上”的编程风格对我有帮助。要使用合并排序的示例:
:)
我可能会滥用这个词,但这对我来说就像是自下而上的设计。函数式编程 与面向对象编程不同,但在两者之间切换时,不需要完全放弃现有的设计技术。
答案 3 :(得分:2)
我发现自己常常迷失在诸如“我应该使用帮助列表进行中间结果存储吗?”等决策中。并且“我应该创建一个辅助函数来执行此操作吗?”
我对此的建议:阅读The Little Schemer。你可以在Erlang中关注它。这是一本很好的书,可以将这种感觉融入到您的手中。
答案 4 :(得分:0)
习惯于认为数据可以用作代码,而反之亦然,这是重要的。
通常你使用几个原始操作(折叠,嵌套,线程,分发......,以及一些是通用内部产品,外部产品等)构建一个程序(数据),并使用这个程序(数据)来操纵其他数据。
过了一会儿,我发现功能正常 编程[...]鼓励“顶级 “设计。
我同意。