Думайте о фолде как о наборе терминов:
[a,b,c,d]
и добавление начального значения zz
и двоичного оператора <+>
между терминами:
foldl (<+>) zz [a,b,c,d] = (((zz <+> a) <+> b) <+> c) <+> d
foldr (<+>) zz [a,b,c,d] = a <+> (b <+> (c <+> (d <+> zz)))
Обратите внимание, что начальное значение также является значением, которое будет иметь сгиб при применении к пустому списку, так что это обычно легко выяснить. Сложная часть - определение соответствующего бинарного оператора.
Итак, чтобы выразить runLengthEncode
как сгиб справа, вам нужно:
'a' <+> ('a' <+> ('a' <+> ('b' <+> zz))) = [(3,'a'),(1,'b')]
для оператора <+>
и некоторого начального значения zz
.
Мы можем легко «решить» для zz
, потому что мы знаем, что runLengthEncode [] = []
, поэтому zz = []
. Нам нужно определить <+>
, чтобы он удовлетворял уравнениям, полученным из примера выше (работает справа налево), например:
'b' <+> [] = [(1, 'b')]
'a' <+> [(1, 'b')] = [(1, 'a'), (1, 'b')]
'a' <+> [(1, 'a'), (1, 'b')] = [(2, 'a'), (1, 'b')]
'a' <+> [(2, 'a'), (1, 'b')] = [(3, 'a'), (1, 'b')]
Определить такого оператора на самом деле довольно просто:
(<+>) :: Char -> [(Int, Char)] -> [(Int, Char)]
x <+> ((n, y) : rest) | x == y = ((n+1), y) : rest
x <+> rest = (1, x) : rest
так мы получаем:
runLengthEncode' :: String -> [(Int,Char)]
runLengthEncode' = foldr (<+>) []
Попробуйте сделать это и с левой складкой:
(((zz + 'a') <+> 'a') <+> 'a') <+> 'b' => [(3,'a'),(1,'b')]
Вы обнаружите, что когда придет время для определения <+>
, вам придется проверять последний элемент текущего RLE вместо первого. Конечно, это можно сделать, но это немного менее естественно, поэтому я использовал правый сгиб выше.
Для runLengthDecode
это одно и то же дело:
(1, 'h') <+> ((5, 'i') <+> zz) = "hiiiii"
Опять же, определите, каким должно быть zz
. Затем выясните, как написать бинарный оператор для решения:
(5, 'i') <+> zz = "iiiii"
(1, 'h') <+> "iiiii" = "hiiiii"
Спойлеры следуют ...
Конечно, вам на самом деле не нужно использовать синтаксис оператора, поэтому полное решение будет выглядеть так:
runLengthEncode'' :: String -> [(Int,Char)]
runLengthEncode'' = foldr step []
where step x ((n, y) : rest) | x == y = ((n+1), y) : rest
step x rest = (1, x) : rest
runLengthDecode'' :: [(Int,Char)] -> String
runLengthDecode'' = foldr step ""
where step (n, x) str = replicate n x ++ str