功能编程和非函数编程

时间:2008-08-23 14:58:12

标签: functional-programming paradigms glossary

在大学的第二年,我们被“教”了Haskell,我对它几乎一无所知,更不用说函数式编程了。

什么是函数式编程,为什么和/ xor我想在哪里使用它而不是非函数式编程?我认为C是非函数式编程语言是正确的吗?

8 个答案:

答案 0 :(得分:87)

功能语言的一个关键特征是一流功能的概念。我们的想法是,您可以将函数作为参数传递给其他函数,并将它们作为值返回。

函数式编程涉及编写不改变状态的代码。这样做的主要原因是对函数的连续调用将产生相同的结果。您可以使用支持第一类函数的任何语言编写功能代码,但有些语言(如Haskell)不允许您更改状态。事实上,你根本不应该产生任何副作用(比如打印文本) - 听起来它可能完全没用。

Haskell采用了不同的IO方法:monads。这些对象包含由解释器顶层执行的所需IO操作。在任何其他级别,它们只是系统中的对象。

函数式编程有哪些优点?功能编程允许编码具有较少的错误潜力,因为每个组件都是完全隔离的。此外,使用递归和第一类函数可以简单地证明正确性,这通常反映了代码的结构。

答案 1 :(得分:22)

  

什么是函数式编程

目前常用的“函数式编程”有两种不同的定义:

旧的定义(源自Lisp)是函数式编程是关于使用第一类函数进行编程,即函数被视为任何其他值,因此您可以将函数作为参数传递给其他函数,函数可以在它们之间返回函数返回值。这最终导致使用更高阶函数,例如mapreduce(您可能听说过mapReduce作为Google大量使用的单一操作,毫不奇怪,它已经关闭了相对的!)。 .NET类型System.FuncSystem.Action使C#中的高阶函数可用。尽管在C#中currying是不切实际的,但是接受其他函数作为参数的函数是常见的,例如, Parallel.For函数。

年轻的定义(由Haskell推广)是函数式编程也是关于最小化和控制包括变异在内的副作用,即编写通过组合表达式来解决问题的程序。这通常被称为“纯函数式编程”。这可以通过称为“纯功能数据结构”的数据结构的完全不同的方法实现。一个问题是将传统的命令式算法转换为使用纯功能数据结构通常会使性能提高10倍。 Haskell是唯一幸存的纯函数式编程语言,但是这些概念已经渗透到.NET上的Linq等库的主流编程中。

  

我想在哪里使用它而不是非函数式编程

无处不在。 C#中的Lambdas现在已经证明了主要的好处。 C ++ 11有lambdas。现在没有理由不使用高阶函数。如果你可以使用像F#这样的语言,你也会受益于类型推断,自动泛化,currying和部分应用(以及许多其他语言功能!)。

  我认为C是一种非函数式编程语言是正确的吗?

是。 C是一种程序性语言。但是,您可以通过在C中使用函数指针和void *来获得函数式编程的一些好处。

答案 2 :(得分:6)

可能值得在最近发布的CoDe Mag上F# "101"查看这篇文章。

另外,Dustin Campbell has a great blog他在F#上发表了很多关于他的冒险的文章。

我希望你发现这些有用:)

编辑:

另外,仅仅补充一点,我对函数式编程的理解是所有是一个函数,或函数的参数,而不是实例/有状态的对象。但我可能是错的F#是什么的我很想进入,但只是没有时间! :)

答案 3 :(得分:4)

John the Statistician的示例代码没有显示函数式编程,因为当你进行函数式编程时,关键是代码没有任何指派(record = thingConstructor(t)是一个赋值),它没有副作用( localMap.put(record)是一个带有副作用的声明)。作为这两个约束的结果,函数所做的一切都被其参数及其返回值完全捕获。如果您想使用C ++模拟函数式语言,请按照必须查看的方式重写Statistician的代码:

RT getOrCreate(const T thing, 
                  const Function<RT<T>> thingConstructor, 
                  const Map<T,RT<T>> localMap) {
    return localMap.contains(t) ?
        localMap.get(t) :
        localMap.put(t,thingConstructor(t));
}

由于没有副作用规则,每个语句都是返回值的一部分(因此return来自第一个),并且每个语句都是一个表达式。在强制执行函数式编程的语言中,隐含return关键字, if 语句的行为类似于C ++的?:运算符。

此外,一切都是不可变的,所以localMap.put必须创建 localMap 的新副本并返回它,而不是修改原来的 localMap ,一个普通的C ++或Java程序会。根据localMap的结构,副本可以重用原始指针,减少必须复制的数据量。

函数式编程的一些优点包括功能程序更短,更容易修改功能程序(因为没有隐藏的全局效果需要考虑),并且更容易获得程序就在第一时间。

然而,功能程序往往运行缓慢(因为他们必须进行所有复制),并且它们不倾向于与处理内存地址的其他程序,操作系统进程或操作系统良好地交互, little-endian字节块,以及其他特定于机器的非功能位。非互操作性程度倾向于与功能纯度的程度和类型系统的严格性成反比关系。

更流行的函数式语言具有非常非常严格的类型系统。在OCAML中,您甚至不能混合整数和浮点数学,或使用相同的运算符(+用于添加整数,+。用于添加浮点数)。这可能是优点还是劣势,具体取决于您对类型检查器捕获某些类型错误的能力的重视程度。

功能语言也往往具有非常大的运行时环境。 Haskell是一个例外(GHC可执行文件在编译时和运行时几乎和C程序一样小),但SML,Common Lisp和Scheme程序总是需要大量的内存。

答案 4 :(得分:3)

是的,你认为C是一种非功能性语言是正确的。 C是一种程序性语言。

答案 5 :(得分:3)

我更喜欢使用函数式编程来保存自己重复的工作,通过制作一个更抽象的版本然后再使用它。让我举个例子。在Java中,我经常发现自己创建映射来记录结构,从而编写getOrCreate结构。

SomeKindOfRecord<T> getOrCreate(T thing) { 
    if(localMap.contains(t)) { return localMap.get(t); }
    SomeKindOfRecord<T> record = new SomeKindOfRecord<T>(t);
    localMap = localMap.put(t,record);
    return record; 
}

这种情况经常发生。现在,我可以用函数式语言编写

RT<T> getOrCreate(T thing, 
                  Function<RT<T>> thingConstructor, 
                  Map<T,RT<T>> localMap) {
    if(localMap.contains(t)) { return localMap.get(t); }
    RT<T> record = thingConstructor(t);
    localMap = localMap.put(t,record);
    return record; 
}

我永远不必再写一个新的,我可以继承它。但是我可以做一个更好的继承,我可以说在这个东西的构造函数

getOrCreate = myLib.getOrCreate(*,
                                SomeKindOfRecord<T>.constructor(<T>), 
                                localMap);

(其中*是一种“保持此参数打开”符号,这是一种曲线)

然后本地getOrCreate与我在一行中写出整个事物时没有继承依赖性的情况完全相同。

答案 6 :(得分:2)

如果您在F#上寻找好的文字

Expert F#由Don Syme共同撰写。 F#的创造者。他专门研究.NET中的泛型,因此他可以创建F#。

F#是在OCaml之后建模的,所以任何OCaml文本都可以帮助你学习F#。

答案 7 :(得分:1)

我觉得What Is Functional Programming?很有用

  

函数式编程是关于编写纯函数,关于删除   我们尽可能隐藏输入和输出,以便我们尽可能多地   代码尽可能只描述输入和输入之间的关系   输出。

首选显式when参数

public Program getProgramAt(TVGuide guide, int channel, Date when) {
  Schedule schedule = guide.getSchedule(channel);

  Program program = schedule.programAt(when);

  return program;
}

public Program getCurrentProgram(TVGuide guide, int channel) {
  Schedule schedule = guide.getSchedule(channel);

  Program current = schedule.programAt(new Date());

  return current;
}
  

功能语言对副作用有积极的敌意。副作用是复杂性,复杂性是错误,错误是恶魔。功能语言也会帮助你对副作用产生敌意。