Haskell中该单子式字符串分割函数的工作原理是什么?
拆解按空白分割字符串的Haskell无点式函数
先给这个函数补全类型签名,方便理解:
f :: String -> [String] f = takeWhile (not . null) . evalState (repeatM $ modify (drop 1) >> State (break (== ' '))) . (' ' :) where repeatM = sequence . repeat
下面一步步拆解执行逻辑:
1. 整体流程概述
函数是三个操作的管道组合:
- 先给输入字符串前置一个空格
- 用
Statemonad无限执行分割动作,收集每次的结果 - 过滤掉结果列表中的空字符串,得到最终的分割结果
2. 逐个部分解析
前置空格:(' ' :)
作用是统一处理输入的开头——比如输入"hello world"会变成" hello world"。这一步是为了让后续的分割逻辑无需区分"第一个单词没有前置空格"的特殊情况,让所有单词都能被相同的逻辑处理。
核心State动作:repeatM $ modify (drop 1) >> State (break (== ' '))
先看辅助定义repeatM = sequence . repeat:
repeat x会生成一个无限重复x的列表sequence能把列表里的多个State动作串起来,变成一个返回"动作结果列表"的State动作- 所以
repeatM action就是无限重复执行同一个State动作,收集每次的返回值,得到一个无限列表
再看被重复执行的action:modify (drop 1) >> State (break (== ' ')),这是两个State动作的绑定:
modify (drop 1):修改当前的State状态(也就是当前剩余的字符串),把它去掉第一个字符。比如当前状态是" hello world",执行后变成"hello world"。>>:忽略前一个动作的返回值(modify返回()),执行后一个动作。State (break (== ' ')):break (== ' ')是Haskell标准函数,作用是把字符串拆成两部分:从开头到第一个空格(不含空格)的前缀,以及包含空格的后缀。比如"hello world"会被拆成("hello", " world"),没有空格的字符串"world"会被拆成("world", "")。State构造函数把String -> (String, String)这样的函数包装成State动作:执行这个动作时,会拿当前的状态字符串做拆分,返回拆分出的前缀(也就是当前的单词),同时把状态更新为拆分出的后缀。
运行State动作:evalState
evalState stateAction initialState的作用是:用initialState作为初始状态,运行stateAction,最终返回动作的结果(忽略最终的状态)。这里的初始状态就是前面加了空格的输入字符串。
运行后会得到一个无限列表:比如输入" hello world",执行过程会生成["hello", "world", "", "", ...]——当状态变成空串后,后续每次执行都会返回空串。
过滤空串:takeWhile (not . null)
从无限列表中依次取元素,直到遇到空串为止,最终得到非空的单词列表,也就是我们要的分割结果。
3. 用例子完整走一遍流程
以输入"hello world"为例:
- 前置空格后得到初始状态:
s0 = " hello world" - 第一次执行
action:modify (drop 1):状态变为s1 = "hello world"State (break ' '):拆分s1得到("hello", " world"),返回"hello",状态更新为s2 = " world"- 列表第一个元素:
"hello"
- 第二次执行
action:modify (drop 1):状态变为s3 = "world"State (break ' '):拆分s3得到("world", ""),返回"world",状态更新为s4 = ""- 列表第二个元素:
"world"
- 第三次执行
action:modify (drop 1):空串执行drop 1还是空串,状态s5 = ""State (break ' '):拆分空串得到("", ""),返回""- 列表第三个元素:
""
takeWhile (not . null)取前两个元素,最终结果:["hello", "world"]
内容的提问来源于stack exchange,提问作者matt
相关产品推荐
相关产品推荐

