折叠后没有后处理步骤是否可以实现单词功能?
Is implementing the words function possible without a postprocessing step after folding?
Real World Haskell, chapter 4, page 98 of the print询问是否可以使用折叠实现words
,这也是我的问题:
可能吗?如果不是,为什么?如果是,如何?
我想到了以下内容,这是基于每个非 space 应该放在输出列表中的最后一个单词之前的想法(这发生在 otherwise
守卫中) ,并且如果还没有的话,space 应该触发向输出列表附加一个空词(这在 if
-then
-else
中处理).
myWords :: String -> [String]
myWords = foldr step [[]]
where
step x yss@(y:ys)
| x == ' ' = if y == "" then yss else "":yss
| otherwise = (x:y):ys
显然这个解决方案是错误的,因为输入字符串中的前导 spaces 导致输出字符串列表中的前导空字符串。
在上面的 link 中,我已经为其他读者研究了几个建议的解决方案,其中许多与我的解决方案的工作方式类似,但它们通常是“post-process”折叠的输出,例如,如果前导字符串为空,则 tail
对其进行处理。
其他方法使用元组(实际上只是对),因此折叠处理对并且可以很好地处理 leading/trailing spaces.
在所有这些方法中,foldr
(或另一个折叠,fwiw)不是提供开箱即用的最终输出的函数;总有其他事情需要以某种方式调整输出。
因此我回到最初的问题并询问是否真的有可能使用折叠实现 words
(以正确处理 trailing/leading/repeated spaces 的方式)。通过 using folds 我的意思是折叠功能必须是最外层的功能:
myWords :: String -> [String]
myWords input = foldr step seed input
如果我没理解错的话,你的要求包括
(1) words "a b c" == words " a b c" == ["a", "b", "c"]
(2) words "xa b c" == ["xa", "b", "c"] /= ["x", "a", "b", "c"] == words "x a b c"
这意味着我们不能
words = foldr step base
对于任何 step
和 base
。
确实,如果我们有那个,那么
words "xa b c"
= def words and foldr
step 'x' (words "a b c")
= (1)
step 'x' (words " a b c")
= def words and foldr
words "x a b c"
这与 (2) 相矛盾。
您肯定需要在 foldr
之后进行一些 post 处理。
@chi 有一个很好的论点,你不能使用 "a" 折叠来实现 words
,但你确实说过 使用折叠s .
words = filterNull . words1
where
filterNull = foldr (\xs -> if null xs then id else (xs:)) []
words1 = foldr (\c -> if c == ' ' then ([]:) else consHead c) []
consHead c [] = [[c]]
consHead c (xs:xss) = (c:xs):xss
最外层和最内层函数都是折叠。 ;-)
是的。尽管这有点棘手,但如果您专注于 CPS(Continuation Passing Style). I had shown a special kind of chunksOf
之前的功能。
,您仍然可以通过使用单个 foldr
而不是其他任何东西来正确地完成这项工作。
在这种折叠中,我们的累加器,因此折叠的结果是一个函数,我们必须将它应用于身份类型的输入,以便我们得到最终结果。所以这可能算作最终处理阶段,因为我们在这里使用的是单折,并且它的类型包括函数。欢迎讨论:)
ws :: String -> [String]
ws str = foldr go sf str $ ""
where
sf :: String -> [String]
sf s = if s == " " then [""] else [s]
go :: Char -> (String -> [String]) -> (String -> [String])
go c f = \pc -> let (s:ss) = f [c]
in case pc of
"" -> dropWhile (== "") (s:ss)
otherwise -> case (pc == " ", s == "") of
(True, False) -> "":s:ss
(True, True) -> s:ss
otherwise -> (pc++s):ss
λ> ws " a b c "
["a","b","c"]
sf
: 开始的初始函数值。
go
:迭代函数
我们实际上并没有在这里充分利用 CPS 的力量,因为我们手边有前一个字符 pc
和当前字符 c
。它在上面提到的 chunksOf
函数中非常有用,每次元素的升序序列被破坏时将 [Int]
分块到 [[Int]]
。
Real World Haskell, chapter 4, page 98 of the print询问是否可以使用折叠实现words
,这也是我的问题:
可能吗?如果不是,为什么?如果是,如何?
我想到了以下内容,这是基于每个非 space 应该放在输出列表中的最后一个单词之前的想法(这发生在 otherwise
守卫中) ,并且如果还没有的话,space 应该触发向输出列表附加一个空词(这在 if
-then
-else
中处理).
myWords :: String -> [String]
myWords = foldr step [[]]
where
step x yss@(y:ys)
| x == ' ' = if y == "" then yss else "":yss
| otherwise = (x:y):ys
显然这个解决方案是错误的,因为输入字符串中的前导 spaces 导致输出字符串列表中的前导空字符串。
在上面的 link 中,我已经为其他读者研究了几个建议的解决方案,其中许多与我的解决方案的工作方式类似,但它们通常是“post-process”折叠的输出,例如,如果前导字符串为空,则 tail
对其进行处理。
其他方法使用元组(实际上只是对),因此折叠处理对并且可以很好地处理 leading/trailing spaces.
在所有这些方法中,foldr
(或另一个折叠,fwiw)不是提供开箱即用的最终输出的函数;总有其他事情需要以某种方式调整输出。
因此我回到最初的问题并询问是否真的有可能使用折叠实现 words
(以正确处理 trailing/leading/repeated spaces 的方式)。通过 using folds 我的意思是折叠功能必须是最外层的功能:
myWords :: String -> [String]
myWords input = foldr step seed input
如果我没理解错的话,你的要求包括
(1) words "a b c" == words " a b c" == ["a", "b", "c"]
(2) words "xa b c" == ["xa", "b", "c"] /= ["x", "a", "b", "c"] == words "x a b c"
这意味着我们不能
words = foldr step base
对于任何 step
和 base
。
确实,如果我们有那个,那么
words "xa b c"
= def words and foldr
step 'x' (words "a b c")
= (1)
step 'x' (words " a b c")
= def words and foldr
words "x a b c"
这与 (2) 相矛盾。
您肯定需要在 foldr
之后进行一些 post 处理。
@chi 有一个很好的论点,你不能使用 "a" 折叠来实现 words
,但你确实说过 使用折叠s .
words = filterNull . words1
where
filterNull = foldr (\xs -> if null xs then id else (xs:)) []
words1 = foldr (\c -> if c == ' ' then ([]:) else consHead c) []
consHead c [] = [[c]]
consHead c (xs:xss) = (c:xs):xss
最外层和最内层函数都是折叠。 ;-)
是的。尽管这有点棘手,但如果您专注于 CPS(Continuation Passing Style). I had shown a special kind of chunksOf
之前的功能。
foldr
而不是其他任何东西来正确地完成这项工作。
在这种折叠中,我们的累加器,因此折叠的结果是一个函数,我们必须将它应用于身份类型的输入,以便我们得到最终结果。所以这可能算作最终处理阶段,因为我们在这里使用的是单折,并且它的类型包括函数。欢迎讨论:)
ws :: String -> [String]
ws str = foldr go sf str $ ""
where
sf :: String -> [String]
sf s = if s == " " then [""] else [s]
go :: Char -> (String -> [String]) -> (String -> [String])
go c f = \pc -> let (s:ss) = f [c]
in case pc of
"" -> dropWhile (== "") (s:ss)
otherwise -> case (pc == " ", s == "") of
(True, False) -> "":s:ss
(True, True) -> s:ss
otherwise -> (pc++s):ss
λ> ws " a b c "
["a","b","c"]
sf
: 开始的初始函数值。
go
:迭代函数
我们实际上并没有在这里充分利用 CPS 的力量,因为我们手边有前一个字符 pc
和当前字符 c
。它在上面提到的 chunksOf
函数中非常有用,每次元素的升序序列被破坏时将 [Int]
分块到 [[Int]]
。