2010-03-04 57 views
9

我有以下形式的功能使用模式OCaml中或F#匹配一个变量

'a -> ('a * int) list -> int 

let rec getValue identifier bindings = 
    match bindings with 
    | (identifier, value)::tail -> value 
    | (_, _)::tail -> getValue identifier tail 
    | [] -> -1 

我可以告诉大家,identifier不被束缚我会把它喜欢的方式,并作为一个新的变量在匹配表达式内。如何获得identifier是传递给函数的内容?

好的!我用一个模式卫士固定它,即| (i, value)::tail when i = indentifier -> value 但我觉得这比我原本想做它的方式更丑陋(我只使用这些语言,因为它们很漂亮......)。有什么想法吗?

+0

您的原始方法让我想起了Prolog的统一性,它比功能性更具说明性。 – ron 2010-03-08 22:59:30

回答

10

您可以使用F#活动模式创建一个模式,它将完全按照您的需要进行操作。 F#支持采用您匹配的值的参数化活动模式,但也需要一个附加参数。

这里是当value是零,否则成功并返回添加值和指定的参数失败一个非常愚蠢的例子:

let (|Test|_|) arg value = 
    if value = 0 then None else Some(value + arg) 

您可以指定模式匹配这样的参数:

match 1 with 
| Test 100 res -> res // 'res' will be 101 

现在,我们可以很容易地定义一个活动模式,它将比较匹配值和活动模式的输入参数。有源图案返回unit option,这意味着它不结合任何新的值(在上面的例子中,它返回,我们分配给符号res一些值):

let (|Equals|_|) arg x = 
    if (arg = x) then Some() else None 

let foo x y = 
    match x with 
    | Equals y -> "equal" 
    | _ -> "not equal" 

可以使用此作为嵌套模式,因此您应该能够使用Equals活动模式重写您的示例。

+0

有趣的是(一种类型的活动模式的写作必须感觉像样板,但你只写了一次,并多次利用它们)。它们如何显示在模块签名中? – 2010-03-04 22:03:46

+0

“Equals”活动模式是通用的,适用于支持比较的任何类型(对F#中可用类型变量的特殊限制)。活动模式显示为签名中特殊名称的功能。签名看起来像这样:当'a:等于'时,'val(| Equals | _ |):'a - >'a - >单位选项 – 2010-03-04 22:10:18

3

这是一个常见的投诉,但我不认为总体上有一个很好的解决方法;模式守卫通常是最好的折衷方案。但是,在某些特定情况下,还有其他替代方法,例如在F#中标记具有[<Literal>]属性的文字,以便它们可以匹配。

5

这不是直接回答这个问题:如何模式匹配变量的值。但它也不完全无关。

如果您想了解类似于F#或OCaml的类似ML的语言中模式匹配的强大功能,请参阅Moca。你也可以看看Moca生成的代码:)(并不是说编译器在你的背后为你做了很多事情有什么问题,在某些情况下,甚至是可取的,但是很多程序员喜欢感觉他们知道他们写的操作会花费多少)。

+0

+1:cool!看起来像是实施DSL的有趣选择。 – Juliet 2010-03-04 22:24:23

6

功能语言的美丽之一是高阶函数。使用这些函数,我们将递归出来,并专注于你真正想做的事情。这是让你的标识符匹配的第一个元组的值,否则返回-1:

let getValue identifier list = 
match List.tryFind (fun (x,y) -> x = identifier) list with 
    | None  -> -1 
    | Some(x,y) -> y 

//val getValue : 'a -> (('a * int) list -> int) when 'a : equality 

paper格雷厄姆·赫顿是一个伟大的介绍你可以用高阶函数做什么。

+1

*眼睛爆炸*无点式风格很聪明,但无法阅读且无法维护。 'fun x - > x |> List.map fst |> List.filter(fun y - > y = identifier)'表示相同的事物而不牺牲可读性。 – Juliet 2010-03-04 22:22:42

+0

采取的一点。我希望这次修改更好。 :) – 2010-03-04 22:43:48

+0

确实,tryFind将是该语言的最佳用法,因为我只暂时使用-1作为None的替身。但我很高兴我学到了更多关于模式匹配和变量绑定的知识。 – 2010-03-05 08:01:28

2

你试图做的就是所谓的平等模式,它不是由Objective Caml提供的。目标Caml的模式是静态的,纯粹是结构性的。也就是说,值是否与模式匹配取决于值的结构,以及在编译时确定的方式。例如,(_, _)::tail是一个匹配头部为一对的非空列表的模式。 (identifier, value)::tail完全匹配相同的值;唯一的区别是后者绑定了更多名称identifiervalue

虽然有些语言具有平等模式,但有一些不平凡的实际考虑因素会使它们变得麻烦。哪种平等?物理平等(Occam中的==),结构相等(Ocaml中的=)或某种类型相关的自定义相等?此外,在Ocaml中,有一个明确的语法指示,指明哪些名称是绑定器,哪些名称引用了先前绑定的值:模式中的任何小写标识符都是绑定器。这两个原因可以解释为什么Ocaml没有出现平等模式。在Ocaml中表达平等模式的惯用方式是守卫。这样,很明显,匹配不是结构性的,identifier不受此模式匹配的约束,以及哪个相等性正在使用。至于丑陋,那是在旁观者的眼中 - 作为一个习惯性的Ocaml程序员,我觉得平等模式很丑陋(出于上述原因)。

match bindings with 
| (id, value)::tail when id = identifier -> value 
| (_, _)::tail -> getValue identifier tail 
| [] -> -1 

在F#中,你有另一种可能性:active patterns,让您预先定义的模式涉及到一个站点卫士。