我正在尝试修改Haskell中自定义数据类型的一个字段。
以下数据类型与我想要修改的数据类型类似。
type BookTitle = String
type ChapterTitle = String
type Page = String
data Chapter = Chapter ChapterTitle [Page]
data Book = MyData [BookTitle] [Chapter]
我希望能够在书中添加更多章节并修改本书的现有章节。 我已阅读this question and its answers和this book,但无法找到我要找的内容。这可能不改变这些声明吗?我正在使用GHCi。
答案 0 :(得分:2)
我试图让事情变得非常简单 - 目标是使这个工作:
λ> let myBook = beginBook "Haskell"
λ> let myBook' = addChapter (Chapter "Intro" ["Hello","World"]) myBook
λ> let myBook'' = addChapter (Chapter "Two" ["Haskell","is","fun"]) myBook'
λ> let myBook''' = modifyChapter
(\ (Chapter _ pages) -> Chapter "Chapter 2" pages)
"Two" myBook''
λ> myBook'''
Book "Haskell"
[ Chapter "Intro" ["Hello","World"]
, Chapter "Chapter 2" ["Haskell","is","fun"]]
首先我更改了您的定义(为什么在MyData
中使用type = typeConstructor
时使用Chapter
? - 为什么有一本书的标题列表? - 我也是想要show
本书,请添加deriving Show
):
type BookTitle = String
type ChapterTitle = String
type Page = String
data Chapter = Chapter ChapterTitle [Page] deriving Show
data Book = Book BookTitle [Chapter] deriving Show
接下来我添加了一个简单的函数,用它的标题开始一些书:
beginBook :: BookTitle -> Book
beginBook title = Book title []
addChapter
并不困难(请注意我使用++
- 在更大的场景中,您可能希望以前后方式表示章节并重写Show
按需逆转 - 但现在这很好):
addChapter :: Chapter -> Book -> Book
addChapter nextChapter (Book title chapters) =
Book title (chapters ++ [nextChapter])
它只是使用模式匹配来将书籍解构为它的部分,在最后添加新的章节并使用旧标题和新章节重新构建新的Book
。
你没有指定你想如何修改章节所以我选择了一个非常通用的形式:基本上你必须提供一个修改单个章节的函数,接下来要修改章节的名称(以书为结尾)想要修改):
modifyChapter :: (Chapter -> Chapter) -> ChapterTitle -> Book -> Book
modifyChapter modify chapterTitle (Book titles chapters) =
Book titles chapters'
where chapters' = map modifyAll chapters
modifyAll chapter@(Chapter chapterTitle' _)
| chapterTitle == chapterTitle' = modify chapter
| otherwise = chapter
它再次将书解构为它的部分然后通过将chapters
函数映射到它们来更改modifyAll
。
此功能只查找具有正确标题的章节,修改它并使所有其他标题保持不变。
最后,本书再次从它的部分重建 - 标题不变,修改后的章节被放入。
使用它可以轻松编写更具体的修改函数。
我们假设您要更改章节的章节标题 - 您只需将modify
函数写入modifyChapter
:
changeChapterTitle :: ChapterTitle -> ChapterTitle -> Book -> Book
changeChapterTitle oldTitle newTitle book =
modifyChapter (\ (Chapter _ pages) -> Chapter newTitle pages) oldTitle book
使用上述功能:
(!!)
完成了最后一个 - 更好的方法是将data Book = Book BookTitle (Map ChapterIndex Chapter)
与type ChapterIndex = Int
和Map
is Data.Map
一起使用 - 您是否可以重写函数以使用它?请注意,Data.Map
使用与此处非常类似的功能来修改地图(例如adjust
);)镜头(其中提到的)基本上提供了一种抽象机制来帮助您免于编写这样的函数 - 但IMO对于感受这个情人级功能编程的东西并不是一个坏主意
答案 1 :(得分:0)
您可以将自己的图书声明为
data Book = MyData { titles :: [BookTitle], chapters :: [Chapter]}
然后
addChapter chapter book = book { chapters = chapter : chapters book }
然而, 会在更深层次的更新中变得笨拙。 cabal中有一个lens
包,可以解决这个问题。