折叠后无需后处理步骤,是否可以实现此文字功能?


13

《真实世界》 Haskell,印刷版的第98页第4章询问是否words可以使用折叠实现,这也是我的问题:

可能吗?如果没有,为什么?如果是,怎么办?

我基于以下想法提出了以下想法:每个非空格都应该放在输出列表中的最后一个单词之前(这发生在otherwise防护中),并且空格应该触发将空值单词附加到输出列表,如果没有一个已经(这是在处理if- - )。thenelse

myWords :: String -> [String]
myWords = foldr step [[]]
  where
    step x yss@(y:ys)
      | x == ' ' = if y == "" then yss else "":yss
      | otherwise = (x:y):ys

显然,此解决方案是错误的,因为输入字符串中的前导空格会导致输出字符串列表中的一个前导空字符串。

在上面的链接中,我研究了为其他读者准备的几种解决方案,其中许多解决方案与我的解决方案相似,但是它们通常对折页的输出进行“后处理”,例如通过折叠tail处理是一个空的前导字符串。

其他方法使用元组(实际上仅是成对的),以便对折处理成对的并可以很好地处理前导/尾随空间。

在所有这些方法中,foldr(或另一种折衷方式)并不是开箱即用地提供最终输出的功能。总有其他东西必须以某种方式调整输出。

因此,我回到最初的问题,询问是否实际上可以words使用折叠实现(以某种方式正确处理尾随/前导/重复的空格)。通过使用折叠,我的意思是折叠功能必须是最外面的功能:

myWords :: String -> [String]
myWords input = foldr step seed input

Answers:


12

如果我理解正确,您的要求包括

(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

对于任何stepbase

确实,如果我们有那样的话

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


1
我更喜欢这种语言……
Enrico Maria De Angelis

甚至["xa"] == words "xa" == step 'x' (words "a") == step 'x' (words " a") == words "x a" == ["x", "a"],它的优点是可以对任一折叠方向进行有效辩论
Cireo

4

@chi有一个很棒的说法,您不能words使用“ a” fold来实现,但是您确实说过使用fold 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

最外层和最内层函数都是折叠。;-)


我想您知道我的意思,但为挑剔+1:P
Enrico Maria De Angelis

1

是。即使有点棘手,foldr如果您沉迷于CPS(Continuation Passing Style),仍然可以通过使用单个方法来正确完成此工作。我以前显示了一种特殊的chunksOf功能。

在这种折叠中,累加器是有效的,因此折叠的结果是一个函数,我们必须将其应用于同一类输入,以便获得最终结果。因此,这可以算作最终处理阶段,因为我们在这里使用单折,并且其类型包括功能。公开辩论:)

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
                                         (False, False) -> (pc++s):ss
                                         (False, True)  -> (pc++s):ss
                                         (True, False)  -> "":s:ss
                                         (True, True)   -> s:ss

λ> ws "   a  b    c   "
["a","b","c"]

sf :开始的初始函数值。

go :迭代器功能

实际上,我们在这里实际上没有充分利用CPS的功能,因为我们在每一个转弯处都既有先前角色pc又有当前角色c。这在chunksOf上面提到的函数中非常有用,同时每次将元素的升序序列中断时都将a [Int]分成块[[Int]]

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.