通过添加元素修改当前列表 - 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 包围。
我想在名为 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 包围。