2017-01-07 77 views
1

我只是想知道什么是XL变量在这里,它没有在任何地方声明?如果字符串不在列表中,此函数返回NONE。否则,返回原始字符串列表,但没有匹配字符串。SML中的模式匹配?

fun same_string(s1 : string, s2 : string) = 
    s1 = s2 

fun all_except_option(s1: string, lst: string list) = 
    case lst of 
     [] => NONE 
     | x::xl => if same_string(s1, x) (* XL HERE ?? *) 
        then SOME xl 
        else case all_except_option(s1, xl) of 
          NONE => NONE 
          | SOME l => SOME (x::l) 
+1

该行*是* xl的声明,就像'x'一样(为什么你对'xl'感到困惑,而不是'x'?)。 – melpomene

+0

哦,还有x。所以我们可以在不使用val的情况下声明这种方式? –

+1

是的,这就是模式匹配的全部内容:您可以测试数据结构所具有的形状,并通过将其绑定到变量来提取子部分。 – melpomene

回答

3

作为墨尔波墨涅上面评论的,在该线的xl实际上xl声明。

在标准ML,变量的声明(结合)通过使用图案然后被匹配到。即使使用val也是如此;例如,我们可以这样写:

val (x, y) = (1, "y") 

声明x(绑定到1)和y(其绑定到"y")。

case表达式和fn表达式中,模式匹配也作为条件加倍;例如,这样的:

val rec sum = 
    fn nil => 0 
    | h :: t => h + sum t 

创建检查,如果它的参数是nil的功能,在这种情况下,做一件事情,如果与它的参数的形式为value1 :: value2(指非空列表),在这种情况下它做了不同的事情(使用这些值)。 (假设,如果参数既不是的那些形式,那么该函数最终会提高Match;但恰巧,任何值类型int list将perforce具有这两种形式之一,所以函数将不会抛出Match

顺便说一句,请注意,这个例子使用数值标识符nil::,但确实不声明他们。而它确实声明值标识符ht。这种不一致可能看起来很奇怪;原因是nil::的值是构造函数,这意味着它们已经使用datatypedatatype 'a list = nil | :: of 'a * 'a list)声明,所以它们可以在模式中使用。事情是这样的:

val nil = 3 

其实完全是非法的,因为nil是指包括已经宣布的构造和类型的nil3是不相容的。相比之下,如果一个标识符是而不是已经是一个值构造函数,那么出现在一个模式中构成一个声明(即使标识符已经是变量:新声明将简单地隐藏现有的声明)。对于新手来说,这对语言来说可能有点棘手,但有一点经验你会发现它很有意义。

2

这似乎是一个有用的库函数。下面是关于如何改善它的几个指针:

  1. 不要写你自己的same_string功能是完全等同于=运营商。只需使用=运营商。这样做是因为可读性:其他SML程序员将识别=运营商,但会想知道same_string是否做更多。为什么不让功能''a而不仅仅是字符串

  2. (轻微的事情)不要在你的函数返回它们的类型后面命名。类型本身应该是足够的注释。即你不会意外地使用这个功能,并没有意识到它实际上是返回一个'列表选项

  3. 过了一会儿,下面的模式:

    case f x of 
        NONE => NONE 
        | SOME y => SOME (g y) 
    

    变得有点乏味,你想抽象SOME/NONE部离开,因为同样的事情总是会发生的(无论它是一个NONE并没有任何反应,或者这是一个SOME y,发生在y)。使用库函数Option.map与类型( 'A→' B)→ 'a选项→' B选项

    Option.map (fn y => g y) (f x) 
    

    当您在列表中映射它的工作原理就像。它看起来像:

    fun all_except (_, []) = NONE 
        | all_except (x1, x2::xs) = 
        if x1 = x2 
        then SOME xs 
        else Option.map (fn ys => x2::ys) (all_except (x1, xs)) 
    

    ,你可能会粗暴地测试它有点像这样:

    val test_all_except_1 = all_except (4, [1,2,3]) = NONE 
    val test_all_except_2 = all_except (1, [1,2,3]) = SOME [2,3] 
    val test_all_except_3 = all_except (2, [1,2,3]) = SOME [1,3] 
    val test_all_except_4 = all_except (3, [1,2,3]) = SOME [1,2] 
    val test_all_except_5 = all_except (1, [1,1,1]) = SOME [1,1] 
    val test_all_except_6 = all_except (1, [1,2,1]) = SOME [2,1] 
    
  4. 你可能要考虑在curried form编写功能:

    fun curry f x y = f (x, y) 
    
    fun all_except _ [] = NONE 
        | all_except x1 (x2::xs) = 
        if x1 = x2 
        then SOME xs 
        else Option.map (curry op:: x2) (all_except x1 xs) 
    

    这里,all_except将它的两个参数彼此分开,而不是在一个(x1, x2::xs)元组/对中。和值(fn ys => x2::ys)已经使用助手功能curry在下面的步骤改写:

    (fn ys => x2::ys)   (* original *) 
    (fn ys => op:: (x2, ys)) (* converting :: to a function *) 
    (fn ys => curry op:: x2 ys) (* making it a curried function *) 
    (curry op:: x2)    (* performing eta conversion *) 
    

    最后一步似乎神秘。功能curry op:: x2是一个名单,并预先x2它,所以它有'列表→'列表。如果我们暂时称之为f,我们会有(fn ys => f ys),但这只是silly way的编写f