Как я могу разобрать Последовательность к (интервал * интервал) кортеж в SML?

У меня есть последовательность что-то вроде этого "3,4\r\n" , и я хочу преобразовать их в кортеж т.е. (3,4) .

Как мы можем достигнуть этого в SML?

Причина, почему я получаю стоимость последовательности, состоит в том, потому что я читаю файл, который возвращает последовательности как этот.

3

4 ответы

Вам нужен простой анализатор, чтобы достигнуть этого. Соответствующая функция, чтобы разобрать целые числа уже доступна в библиотеке как Int.scan (наряду с друзьями для других типов), но необходимо написать остальным сами. Например:

(* 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'''')

И затем, чтобы разобрать все линии:

(* scanList : ((char, 's) StringCvt.reader -> ('a, 's) StringCvt.reader) -> (char, 's)  StringCvt.reader -> ('a list, 's) StringCvt.reader *)
fun scanList scanElem getc stream =
    case scanElem getc stream
      of NONE => SOME ([], stream)
       | SOME (x, stream') =>
    case scanList scanElem getc stream'
      of NONE => NONE
       | SOME (xs, stream'') => SOME (x::xs, stream'')

Использовать его, например:

val test = "4,5\n2,3\n"
val result = StringCvt.scanString (scanList scanLine) test
(* val result : (int * int) list = [(4, 5), (2, 3)] *)

Как вы видите, код немного повторяющийся. Чтобы избавиться от всего соответствия типов выбора, вы могли написать некоторым основным анализатор combinators :

(* scanCharExpect : char -> (char, 's) StringCvt.reader -> (char, 's) StringCvt.reader *)
fun scanCharExpect expect getc stream =
    case getc stream
      of NONE => NONE
       | SOME (c, stream') =>
         if c = expect then SOME (c, stream') else NONE

(* scanSeq : ((char, 's) StringCvt.reader -> ('a, 's) StringCvt.reader) * ((char, 's) StringCvt.reader -> ('b, 's) StringCvt.reader) -> (char, 's) StringCvt.reader -> ('a * 'b, 's) StringCvt.reader *)
fun scanSeq (scan1, scan2) getc stream =
    case scan1 getc stream
      of NONE => NONE
       | SOME (x1, stream') =>
    case scan2 getc stream'
      of NONE => NONE
       | SOME (x2, stream'') => SOME ((x1, x2), stream'')

fun scanSeqL (scan1, scan2) getc stream =
    Option.map (fn ((x, _), stream) => (x, stream)) (scanSeq (scan1, scan2) getc stream)
fun scanSeqR (scan1, scan2) getc stream =
    Option.map (fn ((_, x), stream) => (x, stream)) (scanSeq (scan1, scan2) getc stream)

(* scanLine : (char, 's) StringCvt.reader -> (int * int, 's) StringCvt.reader *)
fun scanLine getc stream =
    scanSeq (
        scanSeqL (Int.scan StringCvt.DEC, scanCharExpect #","),
        scanSeqL (Int.scan StringCvt.DEC, scanCharExpect #"\n")
    ) getc stream

Есть намного более прохладные абстракции, которые можно построить вдоль этих линий, особенно определяя собственные инфиксные операторы. Но я оставлю его в этом.

Вы могли бы также хотеть обращаться с пробелом между символами. StringCvt.skipWS читатель легко доступен в lib для этого, просто вставьте его в правильные места.

7
добавлено

Следующее - сырой пример того, как это может быть сделано

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 прибыль НИ ОДИН ), и что предполагается, что последовательность всегда содержит \r\n , когда последние два знака удалены, беря подстроку.

Обновление

Очевидно, ответ Rossberg - правильный способ сделать его. Однако, в зависимости от задачи под рукой это может все еще служить примером быстрого и глупого способа сделать его.

4
добавлено
Хорошее хакерское проникновение. Но это бросает меня эта ошибка: Ошибка: развязанная переменная или конструктор: mapPartial. , Но Если я делаю List.mapPartial, я получаю Список вместо кортежа.
добавлено автор Sibi, источник
Так или иначе я могу скопировать матч и преобразовать их в кортеж, не грандиозное предприятие.:)
добавлено автор Sibi, источник
Правда, я открыл модуль списка, проверяя что-то еще, мою ошибку.
добавлено автор Jesper.Reenberg, источник

Следующее должно достигнуть этого.

 exception MyError

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

У PS - не было доступа к переводчику SML на работе. Так, возможно, нуждается в небольших изменениях.

1
добавлено
Даже когда вам действительно нравится это, возвращенный кортеж будет содержать x и y как случайная работа только.
добавлено автор Sibi, источник
Это только работает, если оба числа будут точно одной цифрой долго.
добавлено автор sepp2k, источник
@sepp2k вы правы. Не понял это.
добавлено автор ManojGumber, источник

Вот прямой способ извлечь всех целых без знака от последовательности и возвратить их в списке (преобразовывающий список в кортеж, оставлен как осуществление для читателя).

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 *)
0
добавлено