在大学的第二年,我们被“教”了Haskell,我对此几乎一无所知,甚至对函数式编程一无所知。
什么是函数式编程,为什么和/或在什么地方我想用它代替非函数式编程?我是否认为C是一种非函数式编程语言是正确的?
Answers:
功能语言的一个主要功能是一流功能的概念。这个想法是,您可以将函数作为参数传递给其他函数,然后将它们作为值返回。
函数式编程涉及编写不会更改状态的代码。这样做的主要原因是,对函数的连续调用将产生相同的结果。您可以使用任何支持一流功能的语言编写功能代码,但是有些语言(例如Haskell)不允许您更改状态。实际上,您根本不应该产生任何副作用(例如打印文本)-听起来可能完全没有用。
Haskell则对IO采用了另一种方法:monads。这些对象包含要由解释程序的顶层执行的所需IO操作。在任何其他级别上,它们只是系统中的对象。
函数式编程提供什么优势?由于每个组件都是完全隔离的,因此函数式编程可以减少潜在的错误编码。同样,使用递归和一流的功能还可以提供简单的正确性证明,这些证明通常反映了代码的结构。
什么是函数式编程
目前,“函数式编程”有两种不同的定义:
较早的定义(源自Lisp)是函数编程是关于使用一流函数进行编程,即,将函数与其他任何值一样对待,以便您可以将函数作为参数传递给其他函数,并且函数可以在其返回值中返回函数。这最终会使用高阶函数,例如map
和reduce
(您可能听说过mapReduce
Google大量使用的单个操作,并且毫不奇怪,它是近亲!)。.NET类型System.Func
并System.Action
在C#中提供高阶函数。尽管在C#中不建议使用currying,但是将其他函数作为参数接受的函数是常见的,例如,Parallel.For
函数。
较年轻的定义(由Haskell推广)是函数式编程还涉及最小化和控制包括突变在内的副作用,即编写通过编写表达式来解决问题的程序。这通常被称为“纯函数式编程”。通过对称为“纯功能数据结构”的数据结构的完全不同的方法,可以实现这一点。一个问题是,将传统的命令式算法转换为使用纯功能数据结构通常会使性能降低10倍。Haskell是唯一幸存的纯函数式编程语言,但是这些概念已Linq
通过.NET等库进入了主流编程领域。
我想在哪里使用它而不是非功能性编程
到处。现在,C#中的Lambda表现出了巨大的优势。C ++ 11具有lambda。现在没有理由不使用高阶函数。如果您可以使用F#之类的语言,您还将受益于类型推断,自动泛化,currying和部分应用程序(以及许多其他语言功能!)。
我认为C是一种非功能性编程语言是否正确?
是。C是一种过程语言。但是,通过使用函数指针和void *
C语言,您可以获得函数式编程的一些好处。
也许值得在CoDe Mag最近发布的F#“ 101”上查看这篇文章。
另外,达斯汀·坎贝尔(Dustin Campbell)拥有一个出色的博客,他在该博客上发表了许多有关F#快速入门的文章。
希望您觉得这些有用:)
另外,仅补充一点,我对函数式编程的理解是,一切都是函数,或者函数的参数,而不是实例/有状态的对象。但是我可能错了F#是我渴望进入的东西,但不要有时间!:)
统计学家约翰的示例代码未显示函数式编程,因为在进行函数式编程时,关键是该代码没有ASSIGNMENTS(record = thingConstructor(t)
是赋值),并且没有SIDE EFFECTS(localMap.put(record)
是带有副作用的语句) 。由于这两个约束,函数的所有操作都可以通过其参数和返回值完全捕获。如果您想使用C ++模拟功能语言,请按照看起来的方式重写Statistician的代码:
RT getOrCreate(const T something, const Function <RT <T >> ThingConstructor, const Map <T,RT <T >> localMap){ 返回localMap.contains(t)吗? localMap.get(t): localMap.put(t,thingConstructor(t)); }
由于没有副作用规则的结果,每一个语句是返回值(因此的一部分return
来第一),和每一个语句是一个表达式。在执行功能性程序设计的语言中,return
隐含关键字,并且if语句的行为类似于C ++的?:
运算符。
而且,所有内容都是不可变的,因此localMap.put
必须创建一个localMap的新副本并返回它,而不是像普通的C ++或Java程序那样修改原始的localMap。根据localMap的结构,副本可以将指针重新使用到原始副本中,从而减少了必须复制的数据量。
函数式编程的一些优点包括以下事实:函数式程序更短,并且更容易修改函数式程序(因为没有要考虑的隐藏全局影响),并且更容易在函数式编程中正确设置程序。第一名。
但是,功能性程序往往运行缓慢(因为必须执行所有复制操作),并且它们往往无法与其他程序,操作系统进程或操作系统进行良好的交互,这些程序处理内存地址(低位字节序)字节块和其他机器特定的非功能位。非互操作性程度往往与功能纯度和类型系统的严格程度成反比。
较流行的功能语言具有非常非常严格的类型系统。在OCAML中,您甚至不能混合使用整数和浮点运算,也不能使用相同的运算符(+用于添加整数,+。用于添加浮点数)。根据您对类型检查器捕获某些类型的错误的能力的重视程度,这可能是优点还是缺点。
功能语言也往往具有很大的运行时环境。Haskell是一个例外(在编译时和运行时,GHC可执行文件几乎都与C程序一样小),但是SML,Common Lisp和Scheme程序始终需要大量内存。
我更喜欢使用函数式编程来保存自己的重复工作,方法是制作一个更抽象的版本,然后使用它。让我举个例子。在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与如果我将整个内容写成一行而没有继承依赖性的情况完全相同。
我发现什么是函数编程?有用
函数式编程是关于编写纯函数,关于我们尽可能地删除隐藏的输入和输出,以便我们尽可能多的代码仅描述输入和输出之间的关系。
偏好显式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;
}
功能语言积极地反对副作用。副作用是复杂性,复杂性是错误,错误是魔鬼。功能性语言也将帮助您抵御副作用。
Why do we want successive calls to a function to yield the same result. What is the problem with the way things are in C? I never understood this.