这个Haskell中的单子函数是如何工作的?

4

我很难理解这个函数。

  -- split at whitespace
  -- f "hello world" -> ["hello","world"]

  f =  takeWhile (not . null) . evalState (repeatM $ modify (drop 1) 
    >> State (break (== ' '))) . (' ' :)
    where repeatM = sequence . repeat

我已经在理解状态单子的过程中遇到了困难,而缺乏类型签名和点式风格使其更加混乱。
这是怎么回事?
**摘自此处

5
首先,这段代码真的很老了。需要更新以适应现代版本的transformers/mtl。其次,它的格式编写得非常糟糕。就排版而言,似乎他们在追求最大化的混淆,没有将管道和重要逻辑区分开来,使人难以辨认。 - Carl
@Carl 我猜这就是我挣扎的一部分原因! - matt
1
关于该页面内容中“有用的习语”一词似乎存在严重误解。对wiki.haskell.org上发现的大部分内容要持怀疑态度。 - chepner
1个回答

8

break (== ' ') :: String -> (String, String)会产生输入字符串最左侧没有空格的最长前缀和剩余后缀。显然,我们希望以某种方式迭代此过程,将字符串拆分为单词。

State (break (== ' ')) :: State String String将该函数视为具有状态的计算。请记住State :: (s -> (a, s)) -> State s a,因此输出状态是右侧组件。所以初始状态是输入字符串,而break (== ' ')被视为一个State操作,通过return返回第一个单词并更新状态到剩余后缀。

现在将其与modify (drop 1)组合以删除下一个空格,并重复此过程。请注意,它在开头附加到输入(' ' :),并以modify开始。

(sequence . repeat) (modify (drop 1) >> State (break (== ' ')))
  :: State String [String]
  = do
                                -- current_state = " hello wold"
  modify (drop 1)
                                -- current_state = "hello world"
  s1 <- State (break (== ' '))  -- s1 = "hello"
                                -- current_state = " world"
  modify (drop 1)
                                -- current_state = "world"
  s2 <- State (break (== ' '))  -- s2 = "world"
                                -- current_state = ""
  modify (drop 1)
                                -- current_state = ""
  s3 <- State (break (== ' '))  -- s3 = ""
                                -- current_state = ""
  ...
repeat 构造一个无限列表,其中包含同一两步动作 modify (drop 1) >> State (break (== ' '))sequence 接受一系列操作并运行它们,在列表中收集结果。这里我有些含糊不清,但由于这是“惰性状态单子”(存在着严格的状态单子而这不是),所以事情会变得容易,使得sequence 可以运行一个无限的操作列表,并在需要时生成相应的无限结果列表。
如上所示,您将获得一个无限计算,生成所有State (break (== ' ')) 步骤的无限结果列表。
["hello", "world", "", "", ...]

takeWhile (not . null)很明显就是自描述的: 它会取出列表中非空元素,直到遇到空元素。

["hello", "world"]

1
很好的解释。也许值得补充说明的是,这是一个糟糕的实现;无论你是否喜欢这种风格,当有多个空格时它都不能正确地工作(因为它无法区分两个空格之间的空单词和一旦输入耗尽就不断产生的空字符串的无限序列)。f "hello world"["hello"] - Ben
好的。您能否请给您的函数添加类型签名? - matt
@Ben 是的,这只是为了通过示例来理解一些函数。在这种情况下,有没有办法解决你提到的问题? - matt
@Ben 你可以尝试使用类似于dropWhile isSpace而不是drop 1的方法吗? - matt
@matt 是的,有几个选项,但实际上需要修复它取决于所需的行为。这需要真正思考在所有情况下您实际上想要什么行为(是否希望在空格之间保留空的“单词”,还是删除所有连续的空格?您想在仅空格字符上拆分,还是任何类似于空格的字符或其他内容?等等)。 - Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接