we have seen the zipper in the previous post, now we will see some application and examples of Zippers.
firt, we will examine a simple file system implemented with Zipper.
type Name = String type Data = String data FSItem = File Name Data | Folder Name [FSItem] deriving (Show)
to help understanding and experimenting the file system with we will create the following example folder structure.
myDisk :: FSItem myDisk = Folder "root" [ File "goat_yelling_like_man.wmv" "baaaaaa" , File "pope_time.avi" "god bless" , Folder "pics" [ File "ape_throwing_up.jpg" "bleargh" , File "watermelon_smash.gif" "smash!!" , File "skull_man(scary).bmp" "Yikes!" ] , File "dijon_poupon.doc" "best mustard" , Folder "programs" [ File "fartwizard.exe" "10gotofart" , File "owl_bandit.dmg" "mov eax, h00t" , File "not_a_virus.exe" "really not a virus" , Folder "source code" [ File "best_hs_prog.hs" "main = print (fix error)" , File "random.hs" "main = print 4" ] ] ]
As usual we will create the breadcrumbs to leave us a trail of info that helps us to construct info when we navigate up and down the system .
Here's our breadcrumb type for the file system .
data FSCrumb = FSCrumb Name [FSItem] [FSItem] deriving (Show)
and here is our type synonyms of our zipper
type FSZipper = (FSItem, [FSCrumb])
you may wonder why the FSCrumb is like this, the name is for the name of the parent folder, and the first [FSItem] is for the items before the currently focused item, and the ensuing [FSItem] is the collection of items that comes after the current item.
with this piece of information, we can going back in the hierarchy very easily, we just make the latest breadcrumb and assembles a new focus from the current focus and breadcrubm, like so:
fsUp :: FSZipper -> FSZipper fsUp (item, FSCrumb name ls rs:bs) = (Folder name (ls ++ [item] ++ rs), bs)
and if you want to go deeper into the file system, if you are in the "root" and we want to focus on the "dijon_poupon.doc" . the breadcrub that we leaves is going to include the name "root" along with the items that precede "dijon_poupon.doc" and teh ones that comes after it.
import Data.List (break) fsTo :: Name -> FSZipper -> FSZipper fsTo name (Folder folderName items, bs) = let (ls, item:rs) = break (nameIs name) items in (item, FSCrumb folderName ls rs:bs) nameIs :: Name -> FSItem -> Bool nameIs name (Folder folderName _) = name == folderName nameIs name (File fileName _) = name == fileName
First we use break to break the list of items in a folder into those that precede the file that we're searching for and those that come after it. If you remember, break takes a predicate and a list and returns a pair of lists. The first list in the pair holds items for which the predicate returns False. Then, once the predicate returns True for an item, it places that item and the rest of the list in the second item of the pair. We made an auxilliary function called nameIs that takes a name and a file system item and returns True if the names match.
there is limitation, of course with this simple implementation, Note that if the name we're looking for isn't in the folder, the pattern item:rs will try to match on an empty list and we'll get an error. Also, if our current focus isn't a folder at all but a file, we get an error as well and the program crashes.
let's stretch our muscles.
ghci> let newFocus = (myDisk,[]) -: fsTo "pics" -: fsTo "skull_man(scary).bmp"
we can get the focus (the first component of the zipper) , and see what really it is.
ghci> fst newFocus File "skull_man(scary).bmp" "Yikes!"
and we can also, move up and focus on the neighboring file, here it is :
ghci> let newFocus2 = newFocus -: fsUp -: fsTo "watermelon_smash.gif" ghci> fst newFocus2 File "watermelon_smash.gif" "smash!!"
Manipulating our file system
now we have the ability to navigate through the file system, and we want to endow our file system the ability to manipulate the files.
fsRename :: Name -> FSZipper -> FSZipper fsRename newName (Folder name items, bs) = (Folder newName items, bs) fsRename newName (File name dat, bs) = (File newName dat, bs)
to show how we can use that: , we can name our "pics" to "cspi",
ghci> let newFocus = (myDisk,[]) -: fsTo "pics" -: fsRename "cspi" -: fsUp
how about a function that makes a new item in the current folder, behold.
fsNewFile :: FSItem -> FSZipper -> FSZipper fsNewFile item (Folder folderName items, bs) = (Folder folderName (item:items), bs)
easy as a pie. to show , suppose we add a "pics" folder and then move back up to the front.
ghci> let newFocus = (myDisk,[]) -: fsTo "pics" -: fsNewFile (File "heh.jpg" "lol") -: fsUp
What's really cool about all this is that when we modify our file system, it doesn't actually modify it in place but it returns a whole new file system. That way, we have access to our old file system (in this case, myDisk) as well as the new one (the first component of newFocus)
Watch your step
so far, while walking through our data structure, whether they were binary tree, lists or file system, we didn't really care if we took a step too far and fell off, for instance, our go left function takes a zipper of binary tree and moves the focus to its left sub-tree.
goLeft :: Zipper a -> Zipper a goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs)
things that we don't cover is that if we 're stepping off from is an empty tree, that is , what if it's not a Node but an empty... what we have with our current impl is a runtime error, not some idea situation that we want.
we know that with Maybe, we can have a success status denoted by a Just value and a failure situation denoted by a Nothing maybe type.
let's enrich the code to have the maybe code.
goLeft :: Zipper a -> Maybe (Zipper a) goLeft (Node x l r, bs) = Just (l, LeftCrumb x r:bs) goLeft (Empty, _) = Nothing goRight :: Zipper a -> Maybe (Zipper a) goRight (Node x l r, bs) = Just (r, RightCrumb x l:bs) goRight (Empty, _) = Nothing
now, we won't fell off because we are going too far, instead Nothing sh ould be what we get.
ghci> goLeft (Empty, []) Nothing ghci> goLeft (Node 'A' Empty Empty, []) Just (Empty,[LeftCrumb 'A' Empty])
and we update the goUp method and we have this
goUp :: Zipper a -> Maybe (Zipper a) goUp (t, LeftCrumb x r:bs) = Just (Node x t r, bs) goUp (t, RightCrumb x l:bs) = Just (Node x l t, bs) goUp (_, []) = Nothing
what we have in extra is the last setence, caled
goUp (_, []) = Nothing
now, instead of getting Zipper a, we are getting a Maybe type, and the type in our contex is Maybe (zipper a), but our current function takes only Zipper, so we have to trade in all our -: with >>=, alright, we can chain the operation again!,
ghci> let coolTree = Node 1 Empty (Node 3 Empty Empty) ghci> return (coolTree,[]) >>= goRight Just (Node 3 Empty Empty,[RightCrumb 1 Empty]) ghci> return (coolTree,[]) >>= goRight >>= goRight Just (Empty,[RightCrumb 3 Empty,RightCrumb 1 Empty]) ghci> return (coolTree,[]) >>= goRight >>= goRight >>= goRight Nothing
so, we have equipped our tree with a safety-net that we will catch us should we fall off. The FileSystem we mentioned before do not have this safety-net, we might as well add it back.
相关推荐
“haskell-do::pencil2:-Haskell代码编辑器专注于交互式开发” 这个标题揭示了我们正在讨论一个专为Haskell编程语言设计的代码编辑器,名为“haskell-do::pencil2”。这个编辑器强调的是交互式编程体验,这意味...
【标题】:“yesod-haskell-json-api-starter:使用Postgres的Yesod JSON API模板”是一个基于Haskell编程语言和Yesod框架构建的项目模板,专门用于快速搭建使用PostgreSQL数据库的JSON API服务。 【描述】:这个...
dist/build/site/site watch# builds the site and serves it at localhost:8000# any changes to the content immediately regenerates the relevant parts of the website$ dist/build/site/site b
这个名为"Atom-atom-haskell-pointfree"的压缩包是针对Atom编辑器的一个插件,专门用于Haskell编程语言。插件的核心功能是将Haskell代码中的函数表示从“有指针”形式(pointful)转换为“无指针”形式(pointfree)...
用于 haskell-relational-record 的 MySQL 驱动程序 这个项目被合并到 。 准备 $ git clone git@github.com:khibino/haskell-relational-record.git $ git clone git@github.com:bos/hdbc-mysql.git $ git clone ...
在 Emacs 中,`haskell-mode` 是一个专门为了提升 Haskell 开发体验而设计的模式。 `haskell-mode` 提供了多种增强功能,旨在帮助 Haskell 开发者更高效地编写、调试和理解代码。这个模式包括以下关键特性: 1. **...
mvc-updates-examples v1.0.0 该库包含mvc-updates库的示例。 要安装这个库,首先安装 Haskell 平台,然后运行: $ cabal install $ ~/.cabal/bin/mvc-spreadsheet-example # Run the spreadsheet example $ ~/....
Atom-ide-haskell-hoogle 是一个专门为 Atom 编辑器设计的插件,它整合了 Haskell 的 Hoogle 工具,以提供强大的代码提示和搜索功能。Atom 是一款由 GitHub 开发的开源文本编辑器,它利用现代 web 技术如 HTML、CSS ...
在压缩包“haskell-for-typescript-devs-master”中,你可能找到了一系列的教程、示例代码或者解释文档。这些资料可能包括了: 1. **介绍**:介绍Haskell的历史、哲学和与其他语言的对比,帮助你理解为什么选择学习...
在深入探讨"Haskell-CPU-Instruction-Counter"项目之前,我们先来理解一下涉及的关键概念。Haskell是一种纯函数式编程语言,以其强类型、惰性求值和高阶函数特性而闻名。性能分析是软件开发中的一个重要环节,用于...
"Haskell-Lens-Tutorial-Library"显然是一个旨在弥补官方文档中教程不足的资源,帮助开发者更好地理解和应用Lens库。 首先,我们来了解一下什么是Lens。在Haskell中,一个Lens可以看作是对数据结构中的某个部分的一...
### Haskell - The Craft of Functional Programming, 2ed(带书签) #### 重要内容概览 本书《Haskell - The Craft of Functional Programming, 2ed》是功能编程领域的一本经典著作,由Simon Thompson编写,出版于...
`Haskell-learn-functional-programming`这个项目很可能是包含一系列的示例、练习和教程,可能涵盖上述概念以及更多内容,如类型类、monads、GHCi交互式环境、类型系统高级特性、错误处理、IO操作等。通过学习这个...
从1.0.0开始,haskell-ghc-mod提供haskell-completion-backend服务。 注意:在1.0.0之前,提供了ide-backend服务。 它已被废弃以支持ide-haskell的UPI。 您可以在找到描述 执照 版权所有:copyright:2015 Atom-...
haskell-jp官方网页 服务方式 $ stack install wai-app-static $ stack exec warp -- --host 0.0.0.0 --docroot . 这只是示例方式。 但对于Haskellers来说,这可能是最简单的!
**aws-lambda-haskell-runtime** 是一个专门为 AWS Lambda 服务设计的 Haskell 运行时环境。AWS Lambda 是亚马逊网络服务(Amazon Web Services, AWS)提供的一种无服务器计算平台,允许开发者执行代码而无需预置或...
Haskell型Kong教程学习如何在Haskell中使用带类型的Kong进行编程的练习。... git clone https://github.com/chrisbarrett/haskell-typed-holes-tutorialcd haskell-typed-holes-tutorialstack build
haskell-elm-todo-app 2019年9月24日更新:支持GHC 8.6.5和Elm 0.19 Todo应用在服务器端使用 ( , )构建,在客户端使用构建。 随机笔记(2016) Elm应用程序的模块化结构基于的文章“”。 它还使用的elm-return...
这是用Haskell编写的用于解决SAT的经典DPLL算法的简单实现它使用Happy解析器来解析公式。 要运行它只需 cabal install alexcabal install happycabal configurecabal run < cnf
haskell-neo4j-rest-client Haskell neo4j REST客户端 有关更多文档: : 有关更多用法示例,请查看文件tests / IntegrationTests.hs中的集成测试。