Functional programming “学习建议”;如何思考“功能性”;?

Functional programming “学习建议”;如何思考“功能性”;?,functional-programming,erlang,Functional Programming,Erlang,作为函数式语言的新手(我几周前开始接触Erlang,这是我能接触到的第一种函数式语言) 我开始编写一些小算法(如左旋转列表,气泡排序,合并排序等)。我发现自己经常迷失在决策中,比如“我应该使用助手列表来存储中间结果吗?”和“我应该创建一个助手函数来实现这一点吗?” 过了一段时间,我发现函数式编程(如果我说的毫无意义,请容忍)鼓励“自上而下”的设计:即,当我进行merge_sort时,您首先写下所有合并排序步骤,并将它们命名为单独的helper函数;然后一个接一个地实现这些辅助函数(如果需要进一步

作为函数式语言的新手(我几周前开始接触Erlang,这是我能接触到的第一种函数式语言)

我开始编写一些小算法(如
左旋转列表
气泡排序,
合并排序
等)。我发现自己经常迷失在决策中,比如“我应该使用助手列表来存储中间结果吗?”和“我应该创建一个助手函数来实现这一点吗?”

过了一段时间,我发现函数式编程(如果我说的毫无意义,请容忍)鼓励“自上而下”的设计:即,当我进行merge_sort时,您首先写下所有合并排序步骤,并将它们命名为单独的helper函数;然后一个接一个地实现这些辅助函数(如果需要进一步划分这些辅助函数,请使用相同的方法)

这似乎与OO设计有点矛盾,在OO设计中,您可以从底层开始构建基本的数据结构,然后将数据结构和算法组装成您想要的

谢谢你的评论。是的,我想获得关于如何“用函数式语言思考”(就像“用Java思考”、“用C++思考”)的建议

过了一会儿,我发现函数式编程[…]鼓励“自上而下”的设计

我不确定这是不是一个准确的说法。我最近一直在自学函数式编程,我发现一种“自下而上”的编程风格确实对我有帮助。要使用合并排序示例,请执行以下操作:

  • 首先看看基本情况。如何对0/1元素数组进行排序
  • 接下来,看看base+1,base+2,…案例。最终,您应该看到一种模式(分解为子问题、求解子问题、组合子解决方案),它允许您编写一个通用的递归案例,而不是最终到达基本案例
  • 分解成子问题很容易,但组合子解决方案要困难一些。您需要一种将两个排序数组合并为一个排序数组的方法
  • 现在把所有的东西放在一起。祝贺您,您刚刚编写了合并排序<代码>:)

我可能误用了这个词,但这对我来说就像是自下而上的设计。函数式编程不同于面向对象编程,但在两者之间切换时,您不需要完全放弃现有的设计技术。

答案是函数式编程是使用函数编程,正如它们在数学中定义的那样(简而言之,无副作用的东西将价值从域映射到编码域)。要真正将其转化为“如何思考”是一个很难详尽描述的挥手部分,但我将列举我的一些想法:

  • 定义比效率更重要。也就是说,一个能理解其所有行为的函数的明显正确实现要比一个难以推理的复杂优化函数好。(并且应该尽可能长时间地优先考虑;在有证据之前,必须打破这一优良特性。)
  • 数学函数没有副作用。有用的程序必须有副作用。函数式程序员意识到副作用是非常危险和复杂的事情,并将程序设计为一组函数,从一个副作用中获取输出值,并创建下一个副作用的输入值
  • 第一个是模糊的:“优雅的代码”.List理解可以提供非常简洁的数学表达式,如函数定义。只需看看使用LCs实现的快速排序。这就是我如何定义优雅、简洁并使所有行为清晰明了的方法。而不是perl代码,因为在这里,您最常简洁和晦涩

    第二点是我每天在所有编程中使用的东西。将代码划分为当前状态的函数(方法、例程等),这些函数是无副作用的计算,为下一个操作提供输入(即使下一个操作是什么).返回值后,将其交给执行所述操作的例程,然后重新开始

    在我的头脑中,我将一个Erlang进程描绘成一个状态机图,其中每个顶点都是一个副作用,其输出是从顶点中选择哪条边的函数。函数编程范式教会了我对副作用的高度重视。特别是在Erlang中,因为副作用在并发中确实很重要,而Erlangkes并发非常可用

    同样,一些孤立的部落只有一个单词表示3以上的数字,或者没有单词表示“我的”/“你的”。感觉流行语言没有“这会造成副作用”的单词,但函数式编程有它。这迫使你时刻意识到这一点,这是一件好事

    我发现自己经常迷失在决策中,比如“我应该使用助手列表来存储中间结果吗?”和“我应该创建一个助手函数来实现这一点吗?”

    我的建议是:阅读。你可以在Erlang中遵循它。这是一本很好的书,可以让你了解这一点

    过了一会儿,我发现函数式编程[…]鼓励“自上而下”的设计

    其实,这不是“自上而下”或“自下而上”的设计,而是关注手头问题的“什么”,而不是“如何”.当我开始函数式编程时,我发现我一直在回忆命令式结构,比如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;
    }
    
    assertPrime :: (Integral a) => a -> Bool
    assertPrime x = null divisors
        where divisors = takeWhile (<= div x 2) [y | y <- [2..], mod x y == 0]