72

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

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

4

8 回答 8

91

函数式语言的一个关键特性是一等函数的概念。这个想法是您可以将函数作为参数传递给其他函数并将它们作为值返回。

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

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

函数式编程有什么优势?函数式编程允许以更少的潜在错误进行编码,因为每个组件都是完全隔离的。此外,使用递归和一等函数允许简单的正确性证明,这通常反映了代码的结构。

于 2008-08-23T15:19:17.390 回答
24

什么是函数式编程

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

较早的定义(源自 Lisp)是函数式编程是关于使用一等函数进行编程,即函数被视为任何其他值,因此您可以将函数作为参数传递给其他函数,并且函数可以在其返回值中返回函数。这最终导致使用高阶函数,例如mapand reduce(您可能听说过mapReduceGoogle 大量使用的单个操作,毫不奇怪,它是近亲!)。.NET 类型System.Func并使System.Action高阶函数在 C# 中可用。尽管 currying 在 C# 中是不切实际的,但接受其他函数作为参数的函数很常见,例如Parallel.For函数。

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

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

到处。C# 中的 Lambda 现在已经展示了主要的好处。C++11 有 lambda。现在没有理由不使用高阶函数。如果您可以使用 F# 之类的语言,您还将受益于类型推断、自动泛化、柯里化和部分应用程序(以及许多其他语言功能!)。

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

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

于 2013-01-27T16:15:40.883 回答
6

最近发布的 CoDe Mag上关于F#“101”的这篇文章可能值得一看。

此外,Dustin Campbell 有一个很棒的博客,他在其中发布了许多关于他的冒险与 F# 加速的文章。

我希望你觉得这些有用:)

编辑:

另外,补充一点,我对函数式编程的理解是,一切都是函数,或函数的参数,而不是实例/有状态的对象。但我可能错了 F# 是我渴望进入的东西,但只是不要有时间!:)

于 2008-08-23T15:08:20.897 回答
4

John the Statistician 的示例代码没有展示函数式编程,因为当你在进行函数式编程时,关键是代码没有分配(record = thingConstructor(t)是赋值),并且没有副作用(localMap.put(record)是有副作用的语句) . 由于这两个约束,函数所做的一切都被它的参数和返回值完全捕获。如果您想使用 C++ 模拟函数式语言,则按照它的外观重写 Statistician 的代码:

RT getOrCreate(const T 的东西,
                  const Function<RT<T>> thingConstructor,
                  const Map<T,RT<T>> localMap) {
    返回 localMap.contains(t) ?
        localMap.get(t) :
        localMap.put(t,thingConstructor(t));
}

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

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

函数式编程的一些优点包括函数式程序更短,更容易修改函数式程序(因为没有隐藏的全局影响需要考虑),并且更容易在第一名。

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

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

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

于 2009-02-18T00:27:26.463 回答
3

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

于 2008-08-23T15:10:44.727 回答
3

我更喜欢使用函数式编程来节省自己的重复工作,方法是制作一个更抽象的版本,然后改用它。让我举个例子吧。在 Java 中,我经常发现自己创建映射来记录结构,并因此编写 getOrCreate 结构。

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

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

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

我再也不用写一个新的了,我可以继承它。但是我可以做得比继承更好,我可以在这个东西的构造函数中说

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

(其中 * 是一种“保持此参数打开”表示法,这是一种柯里化)

然后本地 getOrCreate 与我在一行中写出整个内容时完全一样,没有继承依赖关系。

于 2008-08-23T15:31:48.297 回答
2

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

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

F# 以 OCaml 为模型,因此任何 OCaml 文本也可以帮助您学习 F#。

于 2008-08-23T17:54:14.040 回答
1

我发现什么是函数式编程?有用

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

首选显式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;
}

函数式语言对副作用非常不利。副作用是复杂性,复杂性是错误,错误是魔鬼。函数式语言也将帮助您对抗副作用。

于 2016-01-07T08:24:20.110 回答