Functional programming 我如何编写一个没有';是否更改状态(在函数式语言中)?

Functional programming 我如何编写一个没有';是否更改状态(在函数式语言中)?,functional-programming,immutability,Functional Programming,Immutability,我想有一天学习函数式编程,但除了简单的数学,我不明白我怎么能用它来做任何事情 例如:一个简单的web浏览器添加书签功能需要引起某种变化,以便下次用户单击书签时,新书签会出现在列表中。即使在Haskell等纯函数式语言中,您也必须操纵状态。这是通过monad完成的。一个有用的应用程序作为一个整体,通常需要改变一些事情的状态,但这并不意味着所有或您的函数都需要改变状态才能有用 在函数式编程中,表示输入/输出(I/O)操作和状态变化,而不使用引入副作用的语言特性 我认为你只是从面向对象的角度来思考。仅

我想有一天学习函数式编程,但除了简单的数学,我不明白我怎么能用它来做任何事情


例如:一个简单的web浏览器添加书签功能需要引起某种变化,以便下次用户单击书签时,新书签会出现在列表中。

即使在Haskell等纯函数式语言中,您也必须操纵状态。这是通过monad完成的。

一个有用的应用程序作为一个整体,通常需要改变一些事情的状态,但这并不意味着所有或您的函数都需要改变状态才能有用

在函数式编程中,表示输入/输出(I/O)操作和状态变化,而不使用引入副作用的语言特性

我认为你只是从面向对象的角度来思考。仅仅因为一个函数总是在相同的输入下给出相同的输出,并不意味着它不能接受不同类型的输入(可能是无限的输入可能性)并产生不同类型的输出

在函数式编程中,您处理的是不可变对象,而不是可变对象。如果您得到一个对象的输入,您可以创建一个新的修改对象并返回它


看看这个。它包含了一个很好的例子,说明了为什么一个不改变状态的函数是完全有用的

处理“易变性”的好例子。假设您用函数式语言实现了一些数据结构,比如说AVL树。在您实现的函数(插入、删除等)以及内部函数(旋转等)中,您实际上并没有对数据进行变异,而是返回变异后的数据

底层运行时系统确保您的程序有效地使用内存(例如,它执行写时复制和垃圾收集)

在程序的某些部分中,当您真正更改世界状态(I/O、GUI)时,有两种方法

  • 纯函数式语言,如Haskell,将此类操作封装在Monad中(警告:读到它们时,您的头脑可能会爆炸,但不要太担心)
  • 其他函数式语言,如OCaml,允许程序中的可变性和副作用

    • 示例不需要可变状态。对于“添加书签”,可以创建一个新书签列表,该列表的内容与旧列表相同,但有一个新条目。这是大多数不变编程的工作方式;您只需创建一个反映世界新状态的新对象图,而不是对现有对象图进行更改。当一切都不可变时,在“旧”和“新”版本之间很容易共享对象图的大部分

      在您的示例中,假设“浏览器”是具有以下信息的数据结构对象:当前网页、用户主页和书签列表。这可以在内存中表示为一棵树,其外观如下所示:

      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这样的语言中,这就是状态单子的精髓。

      许多其他答案会让你急于使用单子或其他奇异的技术来使用可变状态编程。虽然我亲耳听到Haskell 98报告的编辑将Haskell称为“世界上最好的命令式语言”,但你不需要像其他答案所建议的那样需要太多的可变状态。在函数程序中,将状态保存在函数参数中

      例如,我刚刚完成了一个Haskell程序的编写,该程序决定如何将我的音乐备份到DVD,以便将同一张专辑中的歌曲放在同一张DVD上,并且每一张DVD(最后一张除外)的播放量至少达到99.9%。DVD的列表和专辑的列表,DVD的列表都在不断变化,但没有涉及到参考资料、单子或其他奇异的功能。这些值只是递归函数的参数


      要查看更多的示例和解释,请阅读John Hughes非常好的教程论文。

      此外,在实践中,许多用函数式编程语言编写的应用程序实际上使用了(set!)等副作用函数来实际更改状态。这在理论上并不纯粹,但它确实完成了任务


      (我特别想到的是一款流行的消费类软件,它是用LISP派生语言编写的,但必须在恒定内存中运行,因此垃圾收集之类的东西必须退出窗口。)

      它只是创建了一个新的书签列表。