【问题标题】:Is implementing the words function possible without a postprocessing step after folding?折叠后是否可以在没有后处理步骤的情况下实现单词功能?
【发布时间】:2020-04-29 07:22:21
【问题描述】:

Real World Haskell, chapter 4, page 98 of the print 询问words 是否可以使用折叠来实现,这也是我的问题:

有可能吗?如果不是,为什么?如果是,如何?

我想出了以下内容,这是基于每个非空格都应该添加到输出列表中的最后一个单词的想法(这发生在 otherwise 保护中),并且空格应该触发如果还没有空词,则将空词附加到输出列表(这在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

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

在上面的链接中,我为其他读者研究了几个建议的解决方案,其中许多与我的解决方案类似,但它们通常“后处理”折叠的输出,例如通过 @ 987654328@ing 如果有一个空的前导字符串。

其他方法使用元组(实际上只是对),因此折叠处理对并且可以很好地处理前导/尾随空格。

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

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

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

【问题讨论】:

    标签: haskell functional-programming fold


    【解决方案1】:

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

    (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 之后肯定需要一些后期处理。

    【讨论】:

    • 甚至["xa"] == words "xa" == step 'x' (words "a") == step 'x' (words " a") == words "x a" == ["x", "a"],它的好处是可以作为任一折叠方向的有效参数
    【解决方案2】:

    @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
    

    最外层和最里面的函数都是折叠。 ;-)

    【讨论】:

    • 我想你知道我的意思,但 +1 表示挑剔:P
    【解决方案3】:

    是的。尽管这有点棘手,但您仍然可以通过使用单个 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
                                             (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]]

    【讨论】:

      猜你喜欢
      • 2011-11-20
      • 1970-01-01
      • 1970-01-01
      • 2020-01-02
      • 2013-06-02
      • 1970-01-01
      • 2021-10-11
      • 1970-01-01
      • 2017-03-03
      相关资源
      最近更新 更多