在ML中如何让String.tokens函数输出保留分隔符?
在ML中让String.tokens输出保留分隔符的方法
String.tokens的设计逻辑就是丢弃所有匹配分隔符规则的字符,所以无法直接通过它实现保留分隔符的需求,需要自己实现一个自定义的字符串拆分函数。
原代码及问题
原代码示例:
fun f c = c = #" "; val testStr = "int main(){return 42;}"; val stringL = String.tokens f testStr;
返回结果:
val stringL = ["int","main(){return","42;}"] : string list
期望结果:
val stringL = ["int"," ","main(){return"," ","42;}"] : string list
自定义实现方案
下面是一个能保留分隔符的拆分函数实现:
fun splitWithDelim f s = let fun aux acc current [] = List.rev (if current <> "" then current :: acc else acc) | aux acc current (c::cs) = if f c then let val newAcc = if current <> "" then str c :: current :: acc else str c :: acc in aux newAcc "" cs end else aux acc (current ^ str c) cs in aux "" "" (String.explode s) end (* 测试使用 *) fun isSpace c = c = #" "; val testStr = "int main(){return 42;}"; val stringL = splitWithDelim isSpace testStr;
这段代码运行后会返回你期望的结果:["int"," ","main(){return"," ","42;}"]。
函数逻辑说明
- 用
String.explode把字符串转成字符列表,方便逐个遍历; - 递归函数
aux维护两个状态:acc是已经拆分好的结果列表(逆序存储,最后用List.rev转正),current是正在积累的非分隔符字符串; - 遇到匹配分隔符规则的字符时:
- 如果当前有积累的非分隔符字符串,就把分隔符和积累的字符串依次加入结果列表;
- 如果没有积累的字符串(比如开头就是分隔符),只把分隔符加入结果列表;
- 重置
current为空字符串,继续遍历剩余字符;
- 遇到非分隔符字符时,把字符追加到
current中; - 遍历结束后,把最后剩余的
current(如果非空)加入结果列表,再反转得到最终顺序。
关于管道操作的误区
你提到尝试用管道传递值出错,ML中的|是模式匹配的语法符号,并非其他语言里的管道操作符,所以这种方式无法实现保留分隔符的需求,需要通过自定义递归函数来处理。
内容的提问来源于stack exchange,提问作者Jace Ziegler
相关产品推荐
相关产品推荐

