真正理解程序和功能之间的区别

时间:2011-03-07 22:31:34

标签: programming-languages functional-programming procedural-programming

我真的很难理解程序功能编程范例之间的区别。

以下是维基百科函数式编程条目中的前两段:

  

在计算机科学中,功能性   编程是一种编程范式   将计算视为   数学函数的评估   并避免状态和可变数据。它   强调应用   功能,与之相反   命令式编程风格,其中   强调国家的变化。   功能编程有其根源   在lambda演算中,一个正式的系统   发展于20世纪30年代进行调查   功能定义,功能   应用程序和递归。许多   函数式编程语言可以   被视为对此的详细阐述   lambda calculus。

     

在实践中,a之间的区别   数学函数和概念   命令中使用的“功能”   编程是当务之急   功能可能有副作用,   改变程序状态的值。   因此他们缺乏参考   透明度,即同一种语言   表达式可能导致不同   不同时间的价值取决于   执行程序的状态。   相反,在功能代码中,   函数的输出值取决于   仅限于输入的参数   到函数,所以调用一个函数   f两次使用相同的值   参数x将产生相同的结果   结果f(x)两次。消除   副作用可以使它更容易   理解和预测行为   一个程序,这是关键之一   发展的动机   函数式编程。

在第2段中说

  

相反,在函数代码中,函数的输出值仅取决于输入到函数的参数,因此使用参数f的相同值调用函数x两次将两次产生相同的结果f(x)

这与程序编程的情况不一样吗?

在程序性和功能性方面应该看出什么才能脱颖而出?

9 个答案:

答案 0 :(得分:269)

答案 1 :(得分:45)

功能和命令式编程之间的真正区别在于思维模式 - 命令式程序员正在考虑变量和内存块,而函数式程序员则在思考,“我怎样才能将输入数据转换为输出数据“ - 您的”程序“是数据上的管道和变换集,用于将其从输入转换为输出。这是IMO的有趣部分,而不是“你不应该使用变量”这一点。

由于这种心态,FP程序通常会描述会发生什么,而不是如何将会发生的具体机制 - 这很有用,因为如果我们能够清楚地说明“选择”和“在哪里”和“聚合”意味着,我们可以自由地交换他们的实现,就像我们使用AsParallel()一样,突然我们的单线程应用扩展到 n 核心。

答案 2 :(得分:12)

     Isn't that the same exact case for procedural programming?

不,因为程序代码会产生副作用。例如,它可以在调用之间存储状态。

也就是说,可以在被认为是程序性的语言中编写满足此约束的代码。并且还可以编写在某些被认为有效的语言中打破此约束的代码。

答案 3 :(得分:11)

我不同意WReach的回答。让我们解释他的答案,看看分歧来自何处。

首先,他的代码:

function allOdd(words) {
  var result = true;
  for (var i = 0; i < length(words); ++i) {
    var len = length(words[i]);
    if (!odd(len)) {
      result = false;
      break;
    }
  }
  return result;
}

function allOdd(words) {
  return apply(and, map(compose(odd, length), words));
}

首先要注意的是他正在混淆:

  • 功能
  • 面向表达和
  • Iterator centric

编程,并且缺少迭代样式编程的能力,使其具有比典型功能样式更明确的控制流。

让我们快速谈谈这些。

以表达为中心的风格是尽可能评估事物的事物。虽然函数式语言因其对表达式的热爱而闻名,但实际上可能有一种没有可组合表达式的函数式语言。我要做一个,其中有 no 表达式,只是语句。

lengths: map words length
each_odd: map lengths odd
all_odd: reduce each_odd and

这与之前给出的几乎相同,除了函数纯粹通过语句和绑定链链接。

迭代器中心编程风格可能是Python采用的风格。让我们使用纯粹迭代,以迭代器为中心的样式:

def all_odd(words):
    lengths = (len(word) for word in words)
    each_odd = (odd(length) for length in lengths)
    return all(each_odd)

这不起作用,因为每个子句都是一个迭代过程,它们通过显式暂停和重新开始堆栈帧绑定在一起。语法可以部分地来自功能语言,但是它应用于它的完全迭代的实施例。

当然,你可以压缩它:

def all_odd(words):
    return all(odd(len(word)) for word in words)

现在势在必行看起来不那么糟糕,是吗? :)

最后一点是关于更明确的控制流程。让我们重写原始代码来使用它:

function allOdd(words) {
    for (var i = 0; i < length(words); ++i) {
        if (!odd(length(words[i]))) {
            return false;
        }
    }
    return true;
}

使用迭代器,您可以:

function allOdd(words) {
    for (word : words) { if (!odd(length(word))) { return false; } }
    return true;
}

那么 是函数语言的重点,如果区别在于:

return all(odd(len(word)) for word in words)
return apply(and, map(compose(odd, length), words))
for (word : words) { if (!odd(length(word))) { return false; } }
return true;


函数式编程语言的主要决定性特征是它将突变作为典型编程模型的一部分。人们经常认为这意味着函数式编程语言没有语句或使用表达式,但这些都是简化。函数式语言将行为声明替换为显式计算,然后语言执行减少。

限制使用此功能子集可以让您对程序的行为有更多保证,这样您就可以更自由地构建它们。

当你有一种函数式语言时,创建新函数通常就像编写密切相关的函数一样简单。

all = partial(apply, and)

如果您没有明确控制函数的全局依赖关系,那么这并不简单,甚至可能不可能。函数式编程的最佳特性是,您可以始终如一地创建更通用的抽象,并相信它们可以组合成更大的整体。

答案 4 :(得分:6)

在程序范式中(我应该说“结构化编程”吗?),你有共享的可变内存和指令,它们以某种顺序(一个接一个地)读/写它。

在功能范例中,你有变量和函数(在数学意义上:变量不随时间变化,函数只能根据输入计算某些东西)。

(这是过于简化的,例如,FPL通常具有处理可变内存的工具,而程序语言通常可以支持更高阶的程序,所以事情并不那么明确;但这应该给你一个想法)

答案 5 :(得分:2)

来自Charming Python: Functional programming in PythonIBM Developerworks确实帮助我理解了差异。

特别是对于稍微了解Python的人来说,本文中的代码示例(在功能和程序上对不同的东西进行了对比)可以澄清程序和函数式编程之间的区别。

答案 6 :(得分:2)

在函数式编程中,为了推理符号(变量或函数名称)的含义,您只需要知道两件事 - 当前范围和符号名称。如果你有一个具有不变性的纯功能语言,这些语言都是“静态”(对于严重超载的名称而言遗憾)概念,这意味着只需查看源代码就可以看到 - 当前范围和名称。

在程序编程中如果你想回答问题x背后的价值是什么,你还需要知道你是如何到达那里的,仅凭范围和名称是不够的。这就是我认为最大的挑战,因为这个执行路径是一个“运行时”属性,并且可以依赖于许多不同的东西,大多数人学习只调试它而不是尝试恢复执行路径。

答案 7 :(得分:1)

我最近一直在考虑Expression Problem方面的差异。 Phil Wadler's description经常被引用,但this question的公认答案可能更容易理解。基本上,似乎命令式语言倾向于选择一种方法来解决问题,而函数式语言倾向于选择另一种方法。

答案 8 :(得分:0)

两种编程范例之间的明显区别是状态。

在函数式编程中,避免状态。简而言之,不会为变量赋值。

示例:

def double(x):
    return x * 2

def doubleLst(lst):
    return list(map(double, action))

但是,过程编程使用状态。

示例:

def doubleLst(lst):
    for i in range(len(lst)):
        lst[i] = lst[i] * 2  # assigning of value i.e. mutation of state
    return lst