如何在SML中将字符串解析为(int*int)元组?

如何在SML中将字符串解析为(int*int)元组?,sml,smlnj,ml,Sml,Smlnj,Ml,我有一个类似这样的字符串“3,4\r\n”,我想将它们转换成一个元组,即(3,4) 我们如何在SML中实现这一点 获取字符串值的原因是因为我正在读取一个返回类似字符串的文件。下面应该可以实现这一点 exception MyError fun convert(s) = case String.explode(s) of x::','::y::_ => (x,y) | _ => raise MyError PS-在工作中无法使用SML口译员。

我有一个类似这样的字符串
“3,4\r\n”
,我想将它们转换成一个元组,即
(3,4)

我们如何在SML中实现这一点


获取字符串值的原因是因为我正在读取一个返回类似字符串的文件。

下面应该可以实现这一点

 exception MyError

 fun convert(s) = 
   case String.explode(s) of
        x::','::y::_ => (x,y)
       | _ => raise MyError

PS-在工作中无法使用SML口译员。因此,可能需要稍作修改

您需要一个简单的解析器来实现这一点。库中已经提供了一个解析整数的适当函数as(以及其他类型的friends),但是您必须自己编写其余的函数。例如:

(* scanLine : (char, 's) StringCvt.reader -> (int * int, 's) StringCvt.reader *)
fun scanLine getc stream =
    case Int.scan StringCvt.DEC getc stream
      of NONE => NONE
       | SOME (x1, stream') =>
    case getc stream'
      of NONE => NONE
       | SOME (c1, stream'') =>
    if c1 <> #"," then NONE else
    case Int.scan StringCvt.DEC getc stream''
      of NONE => NONE
       | SOME (x2, stream''') => 
    case getc stream'''
      of NONE => NONE
       | SOME (c2, stream'''') =>
    if c2 <> #"\n" then NONE else
    SOME ((x1, x2), stream'''')
val test = "4,5\n2,3\n"
val result = StringCvt.scanString (scanList scanLine) test
(* val result : (int * int) list = [(4, 5), (2, 3)] *)
要使用它,例如:

(* scanLine : (char, 's) StringCvt.reader -> (int * int, 's) StringCvt.reader *)
fun scanLine getc stream =
    case Int.scan StringCvt.DEC getc stream
      of NONE => NONE
       | SOME (x1, stream') =>
    case getc stream'
      of NONE => NONE
       | SOME (c1, stream'') =>
    if c1 <> #"," then NONE else
    case Int.scan StringCvt.DEC getc stream''
      of NONE => NONE
       | SOME (x2, stream''') => 
    case getc stream'''
      of NONE => NONE
       | SOME (c2, stream'''') =>
    if c2 <> #"\n" then NONE else
    SOME ((x1, x2), stream'''')
val test = "4,5\n2,3\n"
val result = StringCvt.scanString (scanList scanLine) test
(* val result : (int * int) list = [(4, 5), (2, 3)] *)
如您所见,代码有点重复。要消除所有选项类型的匹配,您可以编写一些基本的解析器组合器:

您可以按照这些思路构建更多更酷的抽象,尤其是在定义自己的中缀运算符时。但我还是不说了


您可能还希望处理令牌之间的空白。读者可以在lib中找到,只需将其插入正确的位置。

下面是一个简单的示例,说明了如何做到这一点

fun toPair s =
    let
      val s' = String.substring(s, 0, size s-2)
    in
      List.mapPartial Int.fromString (String.tokens (fn c => c = #",") s')
    end
但是请注意,mapPartial会丢弃任何无法转换为整数的内容(当
Int.fromString
返回
NONE
时),并且假定字符串始终包含
\r\n
,因为最后两个字符将通过使用子字符串删除

更新


显然,罗斯伯格的答案是正确的。但是,根据手头的任务,这可能仍然是一种快速而愚蠢的方法。

这里有一种简单的方法,可以从字符串中提取所有无符号整数并将其返回到列表中(将列表转换为元组留给读者作为练习)


即使您这样做,返回的元组也只包含x和y作为字符。这只适用于两个数字都只有一位数长的情况。很好的技巧。但是它给我带来了这个错误:
错误:未绑定变量或构造函数:mappatile。
但是如果我使用List.mappatile,我会得到List而不是tuple。没错,我在测试其他东西时打开了List模块,这是我的错误。无论如何,我可以进行模式匹配并将它们转换为tuple,这没什么大不了的。:)使用高阶函数操作资源(例如打开的文件)有点让人神经紧张,因为您永远不知道何时会抛出异常,在这种情况下,不清楚资源是否不会泄漏。有没有比这更重要的一阶替代方法?@pyon,如果您遵循上面的自然风格,其中所有函数都定义为只在完全应用的实体中进行计算,那么异常不应该比其他地方更成为问题。当然,如果有一个有效的系统来检查。。。无论哪种方式,使解析成为一级的唯一方法是不使用多态解析函数。
fun ints_from_str str =
  List.mapPartial
    Int.fromString
    (String.tokens (not o Char.isDigit) str);

ints_from_str " foo 1, bar:22? and 333___  ";

(* val it = [1,22,333] : int list *)