【问题标题】:Folds implementation in HaskellHaskell 中的折叠实现
【发布时间】:2019-03-27 15:52:12
【问题描述】:

我在尝试理解 Haskell 上的折叠实现时遇到了很多问题。我需要使用具有此输出的 fold 的两个函数

> runLengthEncode "aaaaaaabbb"
[(7,'a'),(3,'b')]
> runLengthDecode [(1,'h'), (5,'i')]
"hiiiii"

所以我所做的是首先编写函数,就像我对模式匹配所做的那样(它们有效),但现在我不知道如何使用左折叠或右折叠来“翻译”它。

runLengthEncode :: String -> [(Int,Char)]
runLengthEncode [] = []
runLengthEncode (x:xs) = runLengthEncode 1 x xs
    where
        runLengthEncode n x [] = [(n,x)]
        runLengthEncode n x (y:ys) | x == y = runLengthEncode (n + 1) y ys
                                   | otherwise = (n,x) : runLengthEncode 1 y ys

runLengthDecode :: [(Int,Char)] -> String
runLengthDecode [] = []
runLengthDecode ((a,b):xs) = replicate a b ++ (runLengthDecode xs)

【问题讨论】:

  • 您应该发布您使用折叠的尝试,并解释您遇到的问题。
  • 欢迎来到 SO。如果以下答案之一解决了您的问题,您应该接受它(单击相应答案旁边的复选标记)。这有两件事。它让每个人都知道您的问题已得到解决,并为帮助您的人提供帮助。有关完整说明,请参阅here。 (ref)
  • 您有机会查看我在上述评论中提供的链接吗?通过不接受答案,您可以保持问题的开放性,向社区表明这些问题仍未解决。他们还在吗?如果是这样,为什么在 cmets 中没有后续问题或要求澄清?

标签: function haskell functional-programming refactoring fold


【解决方案1】:

将折叠视为获取术语列表:

[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)))

请注意,初始值也是 fold 应用于空列表时的值,因此通常很容易计算出来。困难的部分是定义适当的二元运算符。

所以,要将runLengthEncode 表示为右折叠,您需要:

'a' <+> ('a' <+> ('a' <+> ('b' <+> zz))) = [(3,'a'),(1,'b')]

对于运算符&lt;+&gt; 和一些初始值zz

我们可以轻松“解决”zz,因为我们知道runLengthEncode [] = [],所以zz = []。我们需要定义&lt;+&gt;,以便它满足从上面的示例(从右到左工作)导出的方程,例如:

'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')]

您会发现在定义&lt;+&gt; 时,您必须检查当前RLE 的last 元素而不是第一个元素。当然可以,但是稍微不自然,这就是我在上面使用右折叠的原因。

对于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

【讨论】:

  • 归纳编程 FTW! ---“必须检查当前 RLE 的最后一个元素”最后 is 首先,只是查看 反转方向... :)
猜你喜欢
  • 2015-08-30
  • 2019-02-02
  • 1970-01-01
  • 1970-01-01
  • 2011-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多