我在Ocaml中遇到一些关于模式匹配的问题。
基本上,我需要编写一个名为reverse的函数,它接受一个列表并检查它是否是颠倒的顺序。
到目前为止:
let rec reversed (x:int list):bool =
match x with
| [] -> true
| y::z::tl -> if y < z then false else reversed tl;
| y::[] -> true;;
有效! (实际上让我惊讶的是:P)但是我看到了一个缺陷,那就是如果没有tl,它将无法匹配。测试此返回true:
reversed [5;4;3;1;2];;
这是完全可以理解的,因为没有更多的尾巴它只是匹配y :: []
我该如何解决这个问题?
PS:这是我与Ocaml的第一天。对不起,如果问题很简单:D
PPS:我故意只使用Core。 (没有模块)
PPPS:我理解如果我做了一些根本错误的事情,请指出。
答案 0 :(得分:4)
你的功能问题在于:
| y::z::tl -> if y < z then false else reversed tl;
让我们看看你的清单:[5; 4; 3; 1; 2]
它以这种方式分解:
| 5::4::[3;1;2]
你比较5和4,然后用[3; 2; 1]调用恢复。这意味着4到3之间的比较没有完成!(你可以尝试使用像[5; 4; 99; 98; 97]这样的列表,这是你有意想不到的结果)。
你应该做的是在z上测试,然后用列表的其余部分调用reverted。但是,如果你尝试类似的东西:
| y::z::_ -> if y < z then false else reversed z;
编译器对你大吼大叫,因为z是一个int(而不是int列表)。要解决此问题,您可以通过在tl前面添加z来“重建”列表:
| y::z::tl -> if y < z then false else reversed (z::tl)
或者在y(包含z)之后命名列表,同时仍然提取z:
| y::(z::_ as tl) -> if y < z then false else reversed tl
至于你对这个问题的猜测,我理解你的逻辑,但实际上它并不是那样的。 []可以在分解中“命名”,就像它是一个常规节点一样。
看看这个例子,一个(坏)函数测试是否到达列表的末尾:
let is_end l = match l with
| a -> false
| [] -> true;;
如果您尝试将其放入口译员,则应收到以下消息:
Warning 11: this match case is unused.
那是因为[]已经在第一场比赛中被抓住了。您可以尝试使用is_end []
,它会返回false。
处理此问题的正确方法是如何在代码中执行此操作:
let is_end l = match l with
| x::_ -> false
| [] -> true;;
答案 1 :(得分:2)
您的程序逻辑错误。您想检查列表是否反转(减少?)。但是你的程序输入失败
[5;3;4;2;1]
告诉你它正在逆转/减少。这是因为你太早掉了3。你的中间条款应该是:
| y::z::tl -> if y < z then false else reversed (z::tl);
答案 2 :(得分:1)
您应该使用| y::z::tl -> if y < z then false else reversed (z::tl)
。
如果y&gt; z,你不应该从下一轮列表中删除z,因为z尚未与下一个项目进行比较。
此外,您不需要; 。
正确的代码是
let rec reversed = function
| [] -> true
| hd::[] -> true
| hd1::hd2::tl ->
if hd1 < hd2 then false
else reversed (hd2::tl)
答案 3 :(得分:1)
这是另一种使用其他概念来编写它的方法,如图案保护和外卡:
let rec reversed = function
| [] | [_] -> true
| hd1::hd2::_ when hd1 < hd2 -> false
| _::tl -> reversed tl;;
这种情况有一种情况是真的,一种是假的,一种是递归的。