【问题标题】:Variable Hiding in OCaml's Pattern MatchingOCaml 模式匹配中的变量隐藏
【发布时间】:2021-02-05 00:11:44
【问题描述】:

我有一个方法可以返回列表中最常见元素的计数(特别是 char 值列表)

这是我的代码:

let getMostFrequentCharCount li =
    let hashTable = Hashtbl.create (List.length li)
    in
    let rec getMostFrequentCharCount li acc =
        match li with
            [] -> acc
            | head::tail ->
                (match (Hashtbl.find hashTable head) with
                    exception Not_found -> Hashtbl.add hashTable head 1
                    | _ -> let currentFreq = Hashtbl.find hashTable head 
                           in
                           Hashtbl.replace hashTable head (currentFreq+1));
                let currentFreq = Hashtbl.find hashTable head
                in
                if currentFreq > acc
                then
                    getMostFrequentCharCount tail currentFreq
                else
                    getMostFrequentCharCount tail acc
    in
    getMostFrequentCharCount li 0;;

由于某种原因,当我删除第二个模式匹配块(以 match (Hashtbl.find hashTable head) with 开头)周围的括号时,我的编译器抱怨我的累加器 acc 在下一个 if 语句 if currentFreq > acc 中有类型单元,而 @987654325 @ 应该有 int 类型。

为什么括号修复了这个错误?

【问题讨论】:

    标签: ocaml


    【解决方案1】:

    在 OCaml 语法中,match 分支(在-> 之后)包含由; 分隔的表达式的序列。因此,如果没有括号,以下行将被解析为 _match 分支的一部分。

    由于Hashtbl.add 返回单位,此匹配的第一个分支是单位类型。这意味着_ 分支也必须是单元类型。如果没有括号,以下几行会导致类型错误,因为它们不是 unit 类型。

    我使用 ocamlformat 格式化外部 match 带和不带括号。

    这是带括号的格式化代码:

    match li with
    | [] -> acc
    | head :: tail ->
        ( match Hashtbl.find hashTable head with
        | exception Not_found -> Hashtbl.add hashTable head 1
        | _ ->
            let currentFreq = Hashtbl.find hashTable head in
            Hashtbl.replace hashTable head (currentFreq + 1) );
        let currentFreq = Hashtbl.find hashTable head in
        if currentFreq > acc then getMostFrequentCharCount tail currentFreq
        else getMostFrequentCharCount tail acc
    

    这是没有括号的格式化代码:

    match li with
    | [] -> acc
    | head :: tail -> (
        match Hashtbl.find hashTable head with
        | exception Not_found -> Hashtbl.add hashTable head 1
        | _ ->
            let currentFreq = Hashtbl.find hashTable head in
            Hashtbl.replace hashTable head (currentFreq + 1);
            let currentFreq = Hashtbl.find hashTable head in
            if currentFreq > acc then getMostFrequentCharCount tail currentFreq
            else getMostFrequentCharCount tail acc )
    

    我认为这很好地说明了问题。

    【讨论】:

    • 感谢您的解释;这很有帮助!不过我现在想知道,当 if 语句 if currentFreq > acc 不是相应 match 分支的返回语句时,为什么 acc 被假定为单元类型?
    • 考虑上面不带括号的第二个版本。 match 的所有分支(当然)必须具有相同的类型。所以acc 必须有任何嵌套match 的类型。嵌套match 的第一个分支显然具有类型单元。所以acc 必须有类型单元。因为它与currentFreq > acc 中的currentFreq 进行比较,所以它也必须具有int 类型。这是一个类型错误。分析首先确定acc 的单元类型,这就是消息的措辞。
    • 但是如果match分支被定义为由;分隔的1个或多个表达式的序列(在->之后),并且所述序列必须返回与所有其他相同的类型match 块中的分支,为什么acc(在该 if 语句表达式中)被认为是返回值(对于匹配 _ 的分支),当返回值是 getMostFrequentCharCount tail currentFreq 时,该返回值需要是 unit 类型或getMostFrequentCharCount tail acc
    • 在 cmets 下面不可能进行复杂的讨论。如果您愿意,我可以在答案中添加更多内容。但要快速回答:分析必须按某种顺序进行。有三个东西需要是相同的类型:accHashtbl.add,以及你提到的表达式。可以通过假设前两个是相同类型来进行分析。即,它可以从假设 acc 是单元类型开始。如果代码输入正确,则顺序无关紧要。如果不是,您可能会根据分析发生的顺序得到不同的错误。
    • 哦,等等,我想我明白了。没有括号,对应于exception Not_foundmatch 分支返回一个单元类型,因此编译器假定outer match 分支(对应于[])也返回一个单元类型存储在变量acc 中。所以现在编译器已经假定acc 是一个单元类型,当acc 在if 语句中而acc 不是一个int 类型时,编译器会感到困惑。这是你的意思吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 2011-05-31
    相关资源
    最近更新 更多