通过添加元素修改当前列表 - Haskell 101

Modify current list by adding an element - Haskell 101

我想在名为 billy.

的数据类型 Director 变量的 "movies" 列表中添加一个元素
type Name  = String
type Movie = String
data Director = Director {name:: Name, movies::[Movie]}
    deriving (Show)
let billy = Director "Billy J." ["Good movie 1"]

--addMovieToDirector :: Movie -> Director -> Director
addMovieToDirector m (Director n ms) = Director n (m:ms)

问题是之前的函数没有更新 billy 的电影列表,它创建了一个包含所需列表的新 Director(更改未存储在 billy 上)。如何在不创建另一个 Director 的情况下对 billy 的列表进行操作?我知道,Haskell 与常量一起使用,但是每次修改列表时我都应该创建一个不同的 'billy' "variable" 吗?

谢谢!

您想做什么可以描述为"in-place modification",或"using mutable data"。

Haskell有多种方法可以做到这一点。由于就地修改任何东西几乎总是被视为 "side-effect",因此此类事情只能在 IO monad 中完成,或者使用像 unsafePerformIO.

这样的肮脏技巧

这些是有些高级的主题,在初学者水平上,将 Haskell 值视为完全不可变可以说是有益的。

是的,您不能修改变量。实际上根本就没有"variables"。

billy 视为值的名称,而不是变量。

函数在 Haskell 中所能做的就是接受参数,并计算一些结果 ,没有任何副作用

这可能是来自命令式语言的人最大的心理障碍:"how should I work with data if I can't modify it"?

答案是:你应该像巨型流水线一样构建你的程序:原材料(原始数据、初始参数等)在开始时(你调用的第一个函数)就放在流水线上,每个工作站(function) 做一些有用的事情(returns 一个值),消耗前一个工作站的结果。最后,可能会有一些有价值的东西掉线。

我描述的是简单的函数组合:如果您需要在 b 之后、在 a 之后、在值 x 上执行 c 任务,那么您可以写成 (c . b . a) x,或者 c (b (a x)) 或者 c $ b $ a x.

这样,您就可以编写程序而无需 显式更改 任何内容,而仅描述如何从旧内容中创建新内容。

这听起来非常低效,而且实际上,函数式编程对性能有一些影响(更不用说懒惰了)。然而,编译器足够聪明,可以弄清楚用 Haskell 编写的程序的很多事情,并以某些方式对其进行优化。

我希望这一切很快就会有意义。 :)

哦,欢迎来到 Haskell。 ;)

如果出于某种原因你想在你的程序中有一个可变状态,你可以使用一个 State monad。这是一个例子:

module Main where

import Control.Monad.State

type GameValue = Int
type GameState = (Bool, Int)

type Name  = String
type Movie = String
data Director = Director {name:: Name, movies::[Movie]}
    deriving (Show)

addMovieToDirector :: Movie -> Director -> Director
addMovieToDirector m (Director n ms) = Director n (m:ms)


handleDirector :: Name -> State Director Director
handleDirector m = do
    director <- get
    put (addMovieToDirector m director)
    returnDirector

returnDirector = do
    director <- get
    return director

startState = Director "Billy J." ["Good movie 1"]

main = print $ evalState (handleDirector "Good movie 2") startState

打印结果为

Director {name = "Billy J.", movies = ["Good movie 2","Good movie 1"]}

此处 handleDirector 类型 Name -> State Director Director 函数内部有一个类型为 Director 的可变状态和一个类型为 "result" 的值,同样,Director . get 表示获取状态,put 用于更改状态,evalstate 用于 "calculate" 结果,被构造的 State monad 包围。