函数式编程和非函数式编程
-
09-06-2019 - |
题
在大学第二年,我们“教”了 Haskell,我对此几乎一无所知,对函数式编程更是一无所知。
什么是函数式编程,为什么和/或我想在哪里使用它而不是非函数式编程,我认为 C 是一种非函数式编程语言是否正确?
解决方案
函数式语言的一个关键特性是一等函数的概念。这个想法是,您可以将函数作为参数传递给其他函数并将它们作为值返回。
函数式编程涉及编写不改变状态的代码。这样做的主要原因是连续调用函数将产生相同的结果。您可以用任何支持一等函数的语言编写函数代码,但有些语言(例如 Haskell)不允许您更改状态。事实上,你根本不应该产生任何副作用(比如打印出文本)——这听起来好像它可能完全没用。
Haskell 采用了一种不同的 IO 方法:单子。这些对象包含由解释器顶层执行的所需 IO 操作。在任何其他级别,它们只是系统中的对象。
函数式编程有哪些优点?函数式编程可以减少潜在错误的编码,因为每个组件都是完全隔离的。此外,使用递归和一等函数可以简单地证明正确性,这通常反映了代码的结构。
其他提示
什么是函数式编程
如今常用的“函数式编程”有两种不同的定义:
较旧的定义(源自 Lisp)是函数式编程是使用一等函数进行编程,即其中函数被视为与任何其他值一样,因此您可以将函数作为参数传递给其他函数,并且函数可以在其返回值中返回函数。这最终导致使用高阶函数,例如 map
和 reduce
(你可能听说过 mapReduce
作为 Google 大量使用的单一操作,毫不奇怪,它是近亲!)。.NET 类型 System.Func
和 System.Action
使高阶函数在 C# 中可用。尽管柯里化在 C# 中不切实际,但接受其他函数作为参数的函数很常见,例如这 Parallel.For
功能。
较年轻的定义(由 Haskell 流行)是函数式编程也是关于最小化和控制副作用,包括突变,即。编写通过组合表达式来解决问题的程序。这通常被称为“纯函数式编程”。这是通过称为“纯函数数据结构”的截然不同的数据结构方法实现的。一个问题是,将传统的命令式算法转换为使用纯函数式数据结构通常会使性能下降 10 倍。Haskell 是唯一幸存的纯函数式编程语言,但这些概念已经通过诸如 Linq
在网上。
我想在哪里使用它而不是非函数式编程
到处。C# 中的 Lambda 函数现在已展现出重大优势。C++11 有 lambda。现在没有理由不使用高阶函数。如果您可以使用像 F# 这样的语言,您还将受益于类型推断、自动泛化、柯里化和部分应用(以及许多其他语言功能!)。
我认为 C 是一种非函数式编程语言是否正确?
是的。C 是一种过程语言。但是,您可以通过使用函数指针和函数来获得函数式编程的一些好处 void *
在C.
可能值得查看这篇文章 F#“101” 最近在 CoDe Mag 上发布了。
还, 达斯汀坎贝尔有一个很棒的博客 他在其中发表了许多关于他的 F# 快速入门经历的文章。
我希望你觉得这些有用:)
编辑:
另外,补充一下,我对函数式编程的理解是 一切 是一个函数或函数的参数,而不是实例/有状态对象。但我可能是错的 F# 是我非常想参与但只是没有时间的东西!:)
统计学家约翰的示例代码没有显示函数式编程,因为当您进行函数式编程时,关键是代码不执行任何分配( record = thingConstructor(t)
是一项作业),并且它没有副作用(localMap.put(record)
是一个有副作用的语句)。由于这两个限制,一切 功能 does 完全被它的参数和返回值捕获。如果您想使用 C++ 模拟函数式语言,请按其外观重写统计学家的代码:
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
关键字是隐含的,并且 如果 语句的行为类似于 C++ ?:
操作员。
而且,一切都是不可变的,所以 localMap.put
必须创建一个新副本 本地地图 并返回它,而不是修改原来的 本地地图, ,就像普通的 C++ 或 Java 程序一样。根据 localMap 的结构,副本可以重复使用原始指针,从而减少必须复制的数据量。
函数式编程的一些优点包括函数式程序更短,更容易修改函数式程序(因为没有隐藏的全局效应需要考虑),并且更容易在第一名。
然而,函数式程序往往运行缓慢(因为它们必须进行所有复制),并且它们往往不能与处理内存地址、小端字节序的其他程序、操作系统进程或操作系统进行良好的交互。字节块和其他机器特定的非功能位。不可互操作性的程度往往与功能纯度和类型系统的严格程度成反比。
更流行的函数式语言具有非常非常严格的类型系统。在 OCAML 中,您甚至不能混合整数和浮点数学,或使用相同的运算符(+ 用于添加整数,+.用于添加浮动)。这可能是优点,也可能是缺点,具体取决于您对类型检查器捕获某些类型的错误的能力的重视程度。
函数式语言也往往具有非常大的运行时环境。Haskell 是一个例外(GHC 可执行文件在编译时和运行时几乎和 C 程序一样小),但 SML、Common Lisp 和 Scheme 程序总是需要大量内存。
是的,你认为 C 是一种非函数式语言是正确的。C 是一种过程语言。
我更喜欢使用函数式编程来节省自己的重复工作,方法是制作一个更抽象的版本,然后使用它来代替。让我举个例子。在 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 与我在一行中写出整个内容时完全相同,没有继承依赖项。
如果您正在寻找有关 F# 的好文本
专家 F# 是由唐·赛姆 (Don Syme) 合写的。F# 的创建者。他专门研究 .NET 中的泛型,以便能够创建 F#。
F# 是根据 OCaml 建模的,因此任何 OCaml 文本也可以帮助您学习 F#。
我发现 什么是函数式编程? 有用
功能编程是关于编写纯函数,尽可能地删除隐藏的输入和输出,以便尽可能多的代码只描述输入和输出之间的关系。
更喜欢明确的 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;
}
函数式语言积极抵制副作用。副作用就是复杂性,复杂性就是错误,错误就是魔鬼。函数式语言也将帮助您抵制副作用。