我想有一天学习函数式编程,但我不明白除了简单的数学之外,我怎么能用它来做任何事情。
例如:一个简单的网络浏览器添加书签功能需要引起某种突变,以便下次用户单击书签时,新书签会出现在列表中。
我想有一天学习函数式编程,但我不明白除了简单的数学之外,我怎么能用它来做任何事情。
例如:一个简单的网络浏览器添加书签功能需要引起某种突变,以便下次用户单击书签时,新书签会出现在列表中。
您的示例不需要可变状态。对于“添加书签”,可以创建与旧列表具有相同内容但具有一个新条目的新书签列表。这是大多数不可变编程的工作方式;您只需创建一个反映世界新状态的新对象图,而不是对现有对象图进行更改。当一切都是不可变的时,很容易在“旧”和“新”版本之间共享对象图的大部分。
在您的示例中更深一层,假设“浏览器”是具有以下信息的数据结构对象:当前网页、用户主页和书签列表。这可以在内存中表示为一棵看起来像这样的树:
browser = {
curPage --> "http://current"
homePage --> "http://home"
favorites --> { data --> "http://firstFav"
next --> { data --> "http://secondFav"
next --> null } }
}
现在“AddFavorite”函数将这样的数据结构作为输入并返回一个像这样的新数据结构
browser = *{
curPage --> "http://current"
homePage --> "http://home"
favorites --> *{ data --> *"http://newFav"*
next --> { data --> "http://firstFav"
next --> { data --> "http://secondFav"
next --> null } } }*
}*
标有“*”的位是新对象——收藏夹前面有一个新的链表节点,包含一个新字符串,并且浏览器结构本身是新的(它必须是因为它有一个新的“收藏夹”指针) .
您可以像这样将所有“有状态”计算建模为将“前一个世界”作为输入并返回“下一个世界”作为输出的函数;这是像 Haskell 这样的语言中状态单子的本质。
一个有用的应用程序作为一个整体通常需要改变几件事情的状态,但这并不意味着所有或你的函数都需要改变状态才能有用。
函数式编程中的Monad用于表达输入/输出 (I/O) 操作和状态变化,而不使用引入副作用的语言特性。
我认为您只考虑面向对象。仅仅因为一个函数在给定相同输入的情况下总是给出相同的输出,并不意味着它不能接受不同类型的输入(可能是无限可能的输入)并产生不同类型的输出。
使用函数式编程,您正在处理不可变对象而不是可变对象。如果你得到一个对象的输入,你可以创建一个新的修改过的对象并返回它。
查看Joel 关于软件的 MapReduce 的这篇文章。它包含一个很好的例子,说明为什么不改变状态的函数可以完全有用。
即使在像 Haskell 这样的纯函数式语言中,您也必须操纵状态。这是通过单子完成的。
Many other answers will have you rushing off to use monads or some other exotic techniques to program using mutable state. While I have heard with my own ears the editor of the Haskell 98 Report call Haskell "the world's finest imperative language", you don't need nearly as much mutable state as other answers are suggesting. In a functional program, you keep your state in function parameters.
For example, I've just finished writing a Haskell program that decides how to back up my music to DVDs such that songs from the same album go on the same DVD, and every DVD (except the last) is at least 99.9% full. The list of DVDs and the list of which album goes on which DVD are continually changing, but no references, monads, or other exotic features are involved. These values are simply parameters to a recursive function.
To see some more examples and explanations, read John Hughes's very nice tutorial paper Why Functional Programming Matters.
如何处理“可变性”的好例子。假设你用函数式语言实现了一些数据结构,比如 AVL 树。在您实现的函数(插入、删除等)以及内部函数(旋转等)中,您实际上并没有改变数据,而是返回改变的数据。
底层运行时系统确保您的程序将有效地使用内存(例如,它执行写时复制和垃圾收集)。
当您真正改变世界状态(I/O、GUI)时,在程序的某些部分中,有两种方法。
Also, in practice, many apps written in functional programming languages do in fact make use of side-effect functions like (set! ) to actually change state. It isn't theoretically pure but it certainly gets the job done.
(In particular I'm thinking of a popular piece of consumer software that was written in a LISP derivative but had to run in constant memory, so things like garbage collection had to go out the window.)