Compare commits

...

4 commits

Author SHA1 Message Date
7b4b2593ac Solve Regex task 2024-06-15 22:59:46 +02:00
d6029978c7 Add Regex template 2024-06-15 21:27:05 +02:00
c631790636 Implement file system functions 2024-06-15 21:25:22 +02:00
f71ef5723f Add file system template 2024-06-15 18:16:40 +02:00
2 changed files with 284 additions and 0 deletions

View file

@ -0,0 +1,102 @@
{-
Gegeben ist ein einfacher Datentyp für Verzeichnisstrukturen (Ordnerbäume).
Eine Verzeichnisstruktur besteht in diesem Modell entweder aus einem
einzelnen File (durch seinen Namen und Grösse gegeben), oder durch eine Liste
von Unterverzeichnissen.
-}
type Name = String
type FileSizeKB = Int
data FileSystem
= File Name FileSizeKB
| Dir Name [FileSystem]
deriving Show
example :: FileSystem
example = Dir "root"
[ Dir "desktop"
[ File "notes.txt" 87
, File "brochure.pdf" 581
]
, Dir "pictures"
[ Dir "holiday2019"
[ File "paris1.png" 2075
, File "paris2.png" 3017
]
, Dir "holiday2018"
[ File "rome1.jpg" 2075
, File "rome2.jpg" 4584
, File "rome3.png" 2075
, File "notes.txt" 112
]
]
]
{- Aufgabe:
implementieren Sie eine "allgemeine fold Funktion" wie in der Vorlesung
besprochen.
-}
filesystem :: (Name -> FileSizeKB -> b) -> (Name -> [b] -> b) -> FileSystem -> b
filesystem file dir fs = case fs of
Dir name items ->
dir name (map (filesystem file dir) items)
File name fileSize ->
file name fileSize
{-
Ausgehend von der Funktion 'filesystem' wollen wir nun konkrete Funktionen
implementieren, die uns im Umgang mit dem 'FileSystem' Typ nützlich
erscheinen.
-}
{- Grösse eines Verzeichnisses
Implementieren Sie die Funktion 'size', die die Grösse einer gegebenen
Verzeichnisstruktur zurück gibt. Verwenden Sie die Funktion 'filesystem'.
-}
size :: FileSystem -> Int
size = filesystem (\_ -> \fileSize -> fileSize) (\_ -> \sizes -> sum sizes)
-- >>> size example
-- 14606
--
{- Datei abfragen
Implementieren Sie die Funktion 'existsFile', die bei einer gegebenen
Verzeichnisstruktur zurück gibt, ob darin eine Datei mit dem mitgegebenen
Namen zu finden ist. Verwenden Sie die Funktion 'filesystem'.
-}
existsFile :: Name -> FileSystem -> Bool
existsFile name = filesystem
(\fileName -> \_ -> name == fileName)
(\_ -> \items -> or items)
-- >>> existsFile "rome1.jpg" example
-- True
--
{- Pfade
Ein Pfad ist eine Liste von Namen.
Beispiel: "/pictures/holiday2019/paris1.png" wäre
["pictures", "holiday2019", "paris1.png"]
-}
type Path = [Name]
{- Alle Pfade finden
Implementieren Sie die Funktion 'findAll' mit folgendem Verhalten:
Input: Dateiname, Verzeichnisstruktur
Rückgabe: Alle Pfade in der gegebenen Struktur, die auf eine Datei mit dem
gegebenen Namen zeigen.
Verwenden Sie auch hier die Funktion 'filesystem'.
-}
findAll :: Name -> FileSystem -> [Path]
findAll name = filesystem file dir
where
file :: Name -> Int -> [Path]
file n _ = if n == name then [[n]] else []
dir :: Name -> [[Path]] -> [Path]
dir n subs = map (\path -> [n] ++ path) (concat subs)
-- >>> findAll "notes.txt" example
-- [["root","desktop","notes.txt"],["root","pictures","holiday2018","notes.txt"]]
--

View file

@ -0,0 +1,182 @@
{- Syntax
Implementieren Sie den AST (abstract syntax tree) entsprechend den Vorgaben
auf dem Übungsblatt.
-}
data Regex
= Empty
| Epsilon
| Symbol Char
| Sequence Regex Regex
| Star Regex
| Choice Regex Regex
-- | Sequence, Star, Choice fehlen noch.
deriving Show
{- Semantik
Ziel ist es die Funktion 'match :: Regex -> String -> Bool' zu implementieren,
die überprüft ob ein gegebener String zu einer gegebenen Regex passt. Im
folgenden werden dafür zuerst drei Hilfsfunktionen implementiert, die Sie dann
für 'match' verwenden können.
-}
{- Helper function 1:
Given a predicate and a string this function returns all (proper) tails
of the string that are obtained by removing an initial segment that
satisfies the predicate.
Examples:
tails (\s -> length s == 1) "abcde" == ["bcde"]
tails (\s -> length s >= 1) "abcde" == ["bcde","cde","de","e",""]
-}
tails :: (String -> Bool) -> String -> [String]
tails f s = concatMap
(\x -> if f (take x s) then [(drop x s)] else [])
[0..(length s)]
-- >>> tails (\s -> length s >= 1) "abcde"
-- ["bcde","cde","de","e",""]
--
{- Helper function 2:
By repeatedly calling the tails function, this function checks if
it is possible to partition a given string into consecutive segments
each of which satisfies the predicate.
Examples:
segmentable (\s -> length s == 2) "abcde" == False
segmentable (\s -> length s == 2) "abcd" == True
-}
segmentable :: (String -> Bool) -> String -> Bool
segmentable f s
| s == "" = True
| "" `elem` leads = True
| leads == [] = False
| otherwise = or (map (\x -> segmentable f x) leads)
where
leads = tails f s
-- >>> segmentable (\s -> length s == 2) "abcde"
-- False
--
-- >>> segmentable (\s -> length s == 2) "abcd"
-- True
--
{- Helper function 3:
Given two predicates and a string, this function checks if it is
possible to partition the string into a prefix and a suffix such that
the prefix satisfies the first predicate and the suffix satisfies the
second predicate.
Examples:
combinable (\s -> length s == 2) (\s -> length s == 3) "12345" == True
combinable (\s -> length s == 2) (\s -> length s == 3) "1234" == False
combinable (\s -> length s == 2) (\s -> length s == 3) "123456" == False
-}
combinable :: (String -> Bool) -> (String -> Bool) -> String -> Bool
combinable f g s =
or (
map
(\x -> f (take x s) && g (drop x s))
(reverse [0..len])
)
where
len = length s
-- >>> combinable (\s -> length s == 2) (\s -> length s == 3) "12345"
-- >>> combinable (\s -> length s == 2) (\s -> length s == 3) "1234"
-- >>> combinable (\s -> length s == 2) (\s -> length s == 3) "123456"
-- True
-- False
-- False
--
{-
Matching a particular string to a regex a regex now simply means to check
to cover the base cases and use the helper functions.
-}
match :: Regex -> String -> Bool
match r s = case r of
Empty -> s == ""
Epsilon -> False
Symbol c -> s == [c]
Choice r1 r2 -> (match r1 s || match r2 s)
Sequence r1 r2 -> combinable (match r1) (match r2) s
Star r1 -> segmentable (match r1) s
{-
Examples/test-cases
-}
-- (ab*|ac*)*
r1 :: Regex
r1 = Star
( Choice
( Sequence
( Symbol 'a')
( Star (Symbol 'b'))
)
(Sequence
(Symbol 'a')
(Star (Symbol 'c'))
)
)
-- (ab*|ac*)*x*
r2 :: Regex
r2 = Sequence r1 (Star (Symbol 'x'))
-- ab
r3 :: Regex
r3 = Sequence (Symbol 'a') (Symbol 'b')
-- a(a|b)*
r4 :: Regex
r4 = Sequence (Symbol 'a') $ Star $ Choice (Symbol 'a') (Symbol 'b')
shouldBeTrue1 :: Bool
shouldBeTrue1 = match r1 "abbbbaccca"
-- >>> match r1 "abbbbaccca"
-- True
--
shouldBeFalse1 :: Bool
shouldBeFalse1 = match r1 "abbcc"
-- >>> match r1 "abbcc"
-- False
--
shouldBeTrue2 :: Bool
shouldBeTrue2 = match r2 "abbbbacccabxxxx"
-- >>> match r2 "abbbbacccabxxxx"
-- True
--
shouldBeFalse2 :: Bool
shouldBeFalse2 = match r2 "abbxxxacc"
-- >>> match r2 "abbxxxacc"
-- False
--
shouldBeTrue3 :: Bool
shouldBeTrue3 = match r3 "ab"
-- >>> match r3 "ab"
-- True
--
shouldBeFalse3 :: Bool
shouldBeFalse3 = match r3 "aba"
-- >>> match r3 "aba"
-- False
--
shouldBeTrue4 :: Bool
shouldBeTrue4 = match r4 "abbbab"
-- >>> match r4 "abbbab"
-- True
--
shouldBeFalse4 :: Bool
shouldBeFalse4 = match r4 "abbabc"
-- >>> match r4 "abbabc"
-- False
--