деструктурирующий кортеж с помощью let в ML

#pattern-matching #sml #ml

#сопоставление с образцом #sml #ml

Вопрос:

Могу ли я в sml использовать кортеж в a let ? Если да, то каким будет синтаксис? Я могу потребовать, чтобы это соответствовало концентрическому шаблону, но, похоже, должен быть менее шаблонный способ. В приведенном let ниже примере я хотел бы привязать v1 and v2 к двум значениям кортежа, возвращенным при вызове interpExp . Затем я хотел бы вызвать interpExp одно из этих значений, чтобы получить еще два значения.

 fun performOp (e1,op,e2,table) = 
    let val (v1,t1) = interpExp(e1,table)   (* interpExp returns 2-tuple, bind v1 and t2 to those two values *)
        and val (v2,t2) = interpExp(e1,t1)  (* use t1 which was bound on previous line *)
        and val v3 = (case op of
                           Plus => v1   v2
                     | Minus => v1 - v2
                     | Times => v1 * v2
                     | Div => v1 / v2)
    in (v3,t2)
    end
 

При дальнейших пробах и ошибках кажется, что второй и третий val не нужны, и после этого кажется v1 , что и v2 не входят в область действия следующего предложения.

 fun performOp (e1,op,e2,table) = 
    let val (v1,t1) = interpExp(e1,table) 
        and (v2,t2) = interpExp(e1,t1)  (* oops  t1, not in scope *)
        and v3 = (case op of
                           Plus => v1   v2
                     | Minus => v1 - v2
                     | Times => v1 * v2
                     | Div => v1 / v2)
    in (v3,t2)
    end
 

При дальнейших экспериментах я обнаружил еще один синтаксис, но я не уверен, в чем разница, т. Е. Использование val , а не and в let .

 fun performOp (e1,op,e2,table) = 
    let val (v1,t1) = interpExp(e1,table) 
        val (v2,t2) = interpExp(e1,t1)  (* oops  t1, not in scope *)
        val v3 = (case op of
                           Plus => v1   v2
                     | Minus => v1 - v2
                     | Times => v1 * v2
                     | Div => v1 / v2)
    in (v3,t2)
    end
 

Ответ №1:

Вообще говоря, and ключевое слово используется в SML для введения нескольких взаимно рекурсивных функций или типов данных. Например, мы могли бы определить необоснованно неэффективную проверку на четность как

 fun isEven 0 = true
  | isEven 1 = false
  | isEven n = isOdd (n - 1)

and isOdd 0 = false
  | isOdd 0 = true
  | isOdd n = isEven (n - 1)
 

И пример с типами данных:

 datatype 'a exposed = Exposed of 'a * 'a stream
and      'a stream  = Stream of unit -> 'a exposed
 

Тем не менее, and также поддерживается (я полагаю, ради согласованности?) В некоторых других контекстах, где это просто приводит к одновременному введению всех привязок, связанных вместе. Например, в

 val x = 7
and y = 8
 

оба x и y вводятся одновременно. Теперь перейдем непосредственно к вашим непосредственным примерам:

  1. Первый, который вы представите, столкнется с синтаксической ошибкой, потому что он должен быть and вместо val and
  2. Второй, который вы представляете, вводит все эти привязки одновременно, и, следовательно, вы не можете полагаться t1 на второй — он еще не введен.
  3. Последний правильный в отношении использования val

Однако все они также страдают от синтаксических ошибок, поскольку op также являются зарезервированным словом (оно в основном используется для разрешения обработки инфиксных функций как префиксных функций, например, в val sum = foldr op 0 ). Если вы измените это, оставив вас с чем-то вроде

 fun performOp (e1, binop, e2, table) = 
    let val (v1, t1) = interpExp (e1, table) 
        val (v2, t2) = interpExp (e1, t1)
        val v3 = (case binop of
                   Plus  => v1   v2
                 | Minus => v1 - v2
                 | Times => v1 * v2
                 | Div   => v1 / v2)
    in (v3,t2)
    end
 

тогда он должен работать так, как задумано. Я должен также отметить, что / это используется для деления на real s, поэтому, если вы собираетесь работать с int s вместо этого, оно должно быть div (также инфиксным).

Комментарии:

1. Спасибо за объяснение. op Я не ожидал, что это зарезервированное слово; новичку довольно сложно разобраться. И спасибо за комментарий о / vs div .