As a newbie in functional languages (I started touching Erlang a couple of weeks ago -- the first functional language I could get my hands on).
I started to writing some small algorithms (such as left_rotate_list
, bubble_sort,
merge_sort
etc.). I found myself often getting lost in decisions such as "should I use a helper List for intermediate result storage?" and "should I create a helper function to do this?"
After a while, I found that functional programming (bear with me if what I am talking does not make sense at all) encourages a "top down" design: i.e., when I do merge_sort, you first write down all the merge sort steps, and name them as individual helper functions; and then you implement those helper functions one by one (and if you need to further dividing those helper functions, do it in the same approach).
This seems to contradict OO design a little, in which you can start from the bottom to build the basic data structure, and then assemble the data structure and algorithms into what you want.
Thanks for comments. Yes, I want to get advice about how to "think in functional language" (just like "thinking in Java", "thinking in C++").
5条答案
按热度按时间5lwkijsr1#
An answer is that functional programming is to program using functions, as they are defined in mathematics (in short, side-effect free things that map values from the domain to the codomain). To actually translate that into "how to think" is the hand-waving part that is difficult to be exhaustive about, but I'll sample some of my thoughts:
Number one is associated with the vague: "elegant code". List comprehensions can present very succinct and mathematical-equations like definitions of functions. Just look at the quick-sort implemented with LCs. This is how I define elegance, succinct and makes all behaviours clear. Not that perl code-golf where you are most often terse and cryptic.
Number two is something that I use day to day in all programming. Divide code into functions (methods, routines, etc...) of current state that are side-effect free computations giving inputs to the next action to take (even which the next action to take is). When the value is returned, give it to a routine that performs the action that is described, then start over.
In my head I diagram an Erlang process as a state machine graph, where each vertex is a side-effect and a function whose output is which edge to chose out of the vertex. The high regard of side-effects is something the functional programming paradigm taught me. Especially in Erlang, since side-effects really matter in concurrency, and Erlang makes concurrency very available.
The same way some isolated tribes have only one word for numbers above 3, or no words for "mine"/"yours". It feels like popular languages do not have words for "this will cause a side-effect", but Functional Programming has it. It is forcing you to be aware of it all the time, and that is a good thing.
vbopmzt12#
After a while, I found that functional programming [...] encourages a "top down" design.
Well, it's not about "top down" or "bottom up" design really. It's about focusing on the "what" of the problem at hand, rather than the "how". When I started off with functional programming, I found that I kept recalling imperative constructs like the nested
for
loop in C. Then I quickly found out that trying to translate my imperative thinking to functional constructs was very difficult. I'll try to give you a more concrete example. I'll implement an equivalent program in C and Haskell and attempt to trace my thought process in both cases. Note that I've been explicitly verbose for the purpose of explanation.In C:
Trace of my thought process:
inputNumber
. scanf() written.primeFlag
declared and set equal to1
.primeNumber
against every number from 2 toprimeNumber/2
.for
loop started. Declared a loop variablei
to checkprimeNumber
against.primeNumber
against eachi
. The moment we find even onei
that dividesprimeNumber
, setprimeFlag
to0
andbreak
. Loop body written.for
loop, check the value ofprimeFlag
and report it to the user. printf() written.In Haskell:
Trace of my thought process:
null divisors
.divisors
? First, let's write down a list of possible candidates. Wrote down Texas range from 2 to number/2.mod x y == 0
I want to get advice about how to "think in functional language"
Ok, first and foremost, think "what", not "how". This can take a lot of practice to get used to. Also, if you were formerly a C/C++ programmer like me, stop worrying about memory! Modern languages have a garbage collector, and it's written for you to use- so don't even try to modify variables in place. Another thing that has personally helped me: write down English-like definitions in your program to abstract out the functions that do the heavy-lifting.
sxpgvts33#
过了一段时间,我发现函数式编程[...]鼓励“自上而下”的设计。
我不确定这是不是一个准确的说法。我最近一直在自学函数式编程,我发现一种“自底向上”的编程风格对我很有帮助。用你的合并排序的例子来说:
:)
我可能误用了这个术语,但对我来说,这感觉像是自底向上的设计。函数式编程 * 不同于 * 面向对象编程,但在两者之间切换时,您不应该完全放弃现有的设计技术。
rqenqsqc4#
我发现自己经常迷失在诸如“我是否应该使用helper List来存储中间结果?”和“我是否应该创建一个helper函数来实现这一点?”之类的决策中。
对此我的建议是:读The Little Schemer。你可以用Erlang来理解它。这是一本很好的书,可以让你深入了解它。
qc6wkl3g5#
习惯于认为数据可以用作代码,* 反之亦然 *,这是很重要的。
通常,您使用几个基本操作(折叠、嵌套、线程、分布......,以及一些广义内积、外积等)来构造程序(数据),并使用此程序(数据)来操作其他数据。
过了一段时间,我发现函数式编程[...]鼓励“自上而下”的设计。
我同意。