aboutsummaryrefslogtreecommitdiff
path: root/ch09
diff options
context:
space:
mode:
authorJan Sucan <jan@jansucan.com>2023-11-07 20:20:29 +0100
committerJan Sucan <jan@jansucan.com>2023-11-07 20:20:29 +0100
commit44b2fffa2114d230d1306360e743c23e702d0c9b (patch)
tree8abdca1a04bd3d9f96d163446e17efeaebc0b81e /ch09
parent5f4af007e7f445aaae19629c54063f8912b6f756 (diff)
9_c_3: Add solution
Diffstat (limited to 'ch09')
-rw-r--r--ch09/9_c_3.hs149
-rw-r--r--ch09/test-9_c_3/.svn/svn.jpg0
-rw-r--r--ch09/test-9_c_3/dirA/a.png0
-rw-r--r--ch09/test-9_c_3/dirA/b.jpg0
-rw-r--r--ch09/test-9_c_3/dirB/c.jpg0
-rw-r--r--ch09/test-9_c_3/dirB/d.png0
-rw-r--r--ch09/test-9_c_3/e.jpg0
7 files changed, 149 insertions, 0 deletions
diff --git a/ch09/9_c_3.hs b/ch09/9_c_3.hs
new file mode 100644
index 0000000..bf9bdbf
--- /dev/null
+++ b/ch09/9_c_3.hs
@@ -0,0 +1,149 @@
+-- Write a combinator library that makes it possible to express the kinds of
+-- iterators that foldTree accepts. Does it make the iterators you write any
+-- more succinct?
+
+-- From the assignment it's not clear how the combinators for the iterators
+-- should work.
+--
+-- The operators from the previous exercises in this chapter (>, <, ==, &&, ||)
+-- cannot be directly transferred here because this iterator returns three
+-- values (Done, Skip, Continue) instead just two (True, False). We would have
+-- to define three-value logic system for the Iterate type.
+--
+-- Let's just define a simple (A `andIter` B) combinator for chaining the
+-- iterators as:
+-- 1. Evaluate (A seed info) to resultA
+-- 2. If the resultA is Skip or Done, then return the result
+-- 3. Else if the resultA is (Continue resultSeed) and (resultSeed /= seed),
+-- then return the resultA
+-- 4. Return the result of evaluating (B seed info)
+
+
+{-- From examples/examples/ch09/FoldDir.hs modified according to the assignment
+ --
+ -- Added missing import of takeBaseName.
+ -- Fixed appending correct root path to directory entries when constructing
+ -- their full paths.
+ --}
+import ControlledVisit (Info(..), getInfo, getUsefulContents, isDirectory)
+
+import Control.Monad (liftM)
+import Data.Char (toLower)
+import System.FilePath ((</>), takeFileName, takeExtension, takeBaseName)
+
+data Iterate seed = Done { unwrap :: seed }
+ | Skip { unwrap :: seed }
+ | Continue { unwrap :: seed }
+ deriving (Show)
+
+type Iterator seed = seed -> Info -> Iterate seed
+
+
+foldTree :: Iterator a -> a -> FilePath -> IO a
+
+foldTree iter initSeed path = do
+ endSeed <- fold initSeed path
+ return (unwrap endSeed)
+ where
+ fold seed subpath = getUsefulContents subpath >>= walk seed subpath
+
+ walk seed root (name:names) = do
+ let path' = root </> name
+ info <- getInfo path'
+ case iter seed info of
+ done@(Done _) -> return done
+ Skip seed' -> walk seed' root names
+ Continue seed'
+ | isDirectory info -> do
+ next <- fold seed' path'
+ case next of
+ done@(Done _) -> return done
+ seed'' -> walk (unwrap seed'') root names
+ | otherwise -> walk seed' root names
+ walk seed _ _ = return (Continue seed)
+
+atMostThreePictures :: Iterator [FilePath]
+
+atMostThreePictures paths info
+ | length paths == 3
+ = Done paths
+ | isDirectory info && takeFileName path == ".svn"
+ = Skip paths
+ | extension `elem` [".jpg", ".png"]
+ = Continue (path : paths)
+ | otherwise
+ = Continue paths
+ where extension = map toLower (takeExtension path)
+ path = infoPath info
+{-- End of code from examples --}
+
+
+andIter :: (Eq a) => Iterator a -> Iterator a -> Iterator a
+andIter a b seed info = case resA of
+ Done _ -> resA
+ Skip _ -> resA
+ cont@(Continue newSeed) -> if newSeed /= seed
+ then cont
+ else resB
+ where resA = a seed info
+ resB = b seed info
+
+threePaths :: Iterator [FilePath]
+threePaths paths info
+ | length paths == 3
+ = Done paths
+ | otherwise
+ = Continue paths
+ where path = infoPath info
+
+skipSvn :: Iterator [FilePath]
+skipSvn paths info
+ | isDirectory info && takeFileName path == ".svn"
+ = Skip paths
+ | otherwise
+ = Continue paths
+ where path = infoPath info
+
+includePictures :: Iterator [FilePath]
+includePictures paths info
+ | extension `elem` [".jpg", ".png"]
+ = Continue (path : paths)
+ | otherwise
+ = Continue paths
+ where extension = map toLower (takeExtension path)
+ path = infoPath info
+
+myThreePictures :: Iterator [FilePath]
+myThreePictures = (threePaths `andIter` skipSvn) `andIter` includePictures
+
+
+-- Contents of the test directory:
+-- test-9_c_3/
+-- test-9_c_3/e.jpg
+-- test-9_c_3/dirA
+-- test-9_c_3/dirA/b.jpg
+-- test-9_c_3/dirA/a.png
+-- test-9_c_3/.svn
+-- test-9_c_3/.svn/svn.jpg
+-- test-9_c_3/dirB
+-- test-9_c_3/dirB/d.png
+-- test-9_c_3/dirB/c.jpg
+
+-- ghci> :l 9_c_3.hs
+-- [1 of 2] Compiling ControlledVisit ( ControlledVisit.hs, interpreted )
+-- [2 of 2] Compiling Main ( 9_c_3.hs, interpreted )
+-- Ok, two modules loaded.
+
+-- ghci> foldTree atMostThreePictures [] "test-9_c_3/"
+-- ["test-9_c_3/dirA/a.png","test-9_c_3/dirA/b.jpg","test-9_c_3/e.jpg"]
+
+-- ghci> foldTree myThreePictures [] "test-9_c_3/"
+-- ["test-9_c_3/dirA/a.png","test-9_c_3/dirA/b.jpg","test-9_c_3/e.jpg"]
+
+
+-- It doesn't make the iterators more succinct.
+--
+-- The compound iterator (e.g., myThreePictures) is more readable thanks to
+-- hiding the individual iterators in well-named functions, but because the
+-- individual iterators cannot share common parts (the 'where' clause) the code
+-- duplication cancels the positives.
diff --git a/ch09/test-9_c_3/.svn/svn.jpg b/ch09/test-9_c_3/.svn/svn.jpg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ch09/test-9_c_3/.svn/svn.jpg
diff --git a/ch09/test-9_c_3/dirA/a.png b/ch09/test-9_c_3/dirA/a.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ch09/test-9_c_3/dirA/a.png
diff --git a/ch09/test-9_c_3/dirA/b.jpg b/ch09/test-9_c_3/dirA/b.jpg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ch09/test-9_c_3/dirA/b.jpg
diff --git a/ch09/test-9_c_3/dirB/c.jpg b/ch09/test-9_c_3/dirB/c.jpg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ch09/test-9_c_3/dirB/c.jpg
diff --git a/ch09/test-9_c_3/dirB/d.png b/ch09/test-9_c_3/dirB/d.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ch09/test-9_c_3/dirB/d.png
diff --git a/ch09/test-9_c_3/e.jpg b/ch09/test-9_c_3/e.jpg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ch09/test-9_c_3/e.jpg