【问题标题】:Matching Patterns for list of tuples元组列表的匹配模式
【发布时间】:2019-10-12 15:22:31
【问题描述】:

我正在尝试编写一个 fcn,它接受一个元组列表和一个表达式(我正在研究一个后缀表达式 eval)。这个函数应该遍历表达式并在元组中找到相同的字母。如果匹配,则返回与元组中该字母对应的 int 值。当我运行下面的代码时,我的程序编译并运行,但它在执行期间挂起。我做错了什么?

    let rec getVar ls exp = 

        match ls with
       |head::tl when (fst head) = exp -> printfn "%d" (snd head)
       | _  -> getVar ls exp



    let ls = [("a",5);("b",2);("c",3)]

    let exp = "ab+"
    getVar ls exp

【问题讨论】:

  • 请不要以现有答案不再有意义的方式编辑您的问题。如果您还有其他问题,请提出其他问题,不要重复使用旧问题。

标签: f# pattern-matching


【解决方案1】:

您的match 表达式有一个最终的包罗万象的子句(_ 子句),它只使用相同的参数再次调用getVar。如果when (fst head) = exp 条件失败,则代码将进入 catch-all 子句,因此getVar 会再次使用相同的参数调用,因此第一个条件将失败,因此代码会进入 catch-all 子句,因此getVar 再次被调用...这是一个无限循环。

如果您的 when 条件失败,您可能打算在列表的 tail 上再次调用 getVar

match ls with
|head::tail when (fst head) = exp -> printfn "%d" (snd head)
|head::tail -> getVar tail exp

您还需要考虑如果列表为空(即,您需要与[] 匹配的匹配条件),您将做什么。我会把那个留给你,因为你可以对一个空列表做很多事情,而你想要哪一个并不明显。

【讨论】:

  • 我确实按照建议进行了更改,并且还为空列表案例添加了匹配条件。当我执行时程序仍然挂起。还有其他建议吗?
  • @LongPham 您能否将您的新版本添加为对原始问题的编辑? rmunns 版本(不添加 [] 大小写)应该给出运行时错误,除非列表包含 exp。使用合理的 [] 案例,它应该可以正常工作。
  • @Guran 刚刚添加了一个新的编辑版本!就像我之前说的。我编译了这段代码就好了。当我运行它时,程序挂起并且没有打印出任何值
  • @LongPham 谢谢,但您也应该保留旧版本,因为此编辑使 rmunns 非常有效的答案突然无效。
  • @LongPham - 我看到了您的编辑,您在其中包含了 [] 匹配,并且您遇到了同样的问题:您的 [] 匹配只是再次调用该函数而没有更改任何参数,所以如果代码一旦遇到[] 的情况,就会进入无限循环。您的 [] 案例应该只返回一些值(即使它只是 () 值,意思是“什么都不做”,这可能是您想要的)。
【解决方案2】:

你的比赛必须处理三种情况。

  1. 空列表 -> 返回一些默认值(或没有副作用的单位)
  2. 找到匹配 -> 返回一个值或触发一些副作用。
  3. 尚未找到匹配项 -> 继续在列表尾部搜索。

在您的第一次尝试中,您不小心一直在整个列表中搜索,而不仅仅是在尾部搜索,从而导致无限的递归循环。 在您的第二次尝试中,您改为在空的情况下创建了一个无限循环。 下面是一个如何编写递归函数的示例。

 let rec getVar ls exp =
     match ls with
     |[] -> None
     |head::tail when (fst head) = exp -> Some <| sprintf "%d" (snd head)
     |head::tail  -> getVar tail exp

 let ls = [("a",5);("b",2);("c",3)]

 let result1 = getVar ls "ab+"   // result = None
 let result2 = getVar ls "b"     // result = Some "2"

【讨论】:

  • 感谢您指出这一点!但是,对于 exp "ab+" ,函数应该返回 5 和 2 并忽略运算符。我不明白为什么您的解决方案返回 None
  • 嗯,你正在匹配 fst head 和 exp 之间的相等性。如果元组的第一部分恰好是 exp,那是真的。你的情况是“AB+”。
  • 哦,我明白了。也许我应该比较 exp 的每个字符而不是整个字符串。
  • 是的,显然这段代码距离后缀计算器还有很长的路要走(我认为这是你的目标)。我只是试图解决您问题中的问题,而没有为您编写所有内容;)
【解决方案3】:

getVar 函数的签名错误。最后一个参数应该是表达式的一个字母,而不是整个表达式。调用getVar函数的代码会遍历表达式,对于每个字符,检查是否是字母,如果是则调用getVar,否则执行其他操作。

你的代码被绞死的原因在其他答案中已经解释清楚了,所以我不会在这里重复。但作为一个好习惯,请不要使用| _ -&gt; ...,除非你完全控制了情况。通常,我们应该明确地写出所有的匹配条件,这样如果我们遗漏了什么,编译器就会警告我们。之后,在知道所有条件的情况下,如果真的是“其余条件s”,我们可以使用| _ -&gt; ...

您的getVar 函数可以重写为:

let rec getVar ls letter =
    match ls with
    | [] -> ()
    | head :: tail when fst head = letter -> printfn "%d" (snd head)
    | _ :: tail -> getVar tail letter

建议大家学习一下F#核心库的常用内置函数。所以你可以在你的问题中使用List.tryFind 函数:

let getVar ls letter =
    ls |> List.tryFind (fun (key, value) ->
                 if key = letter then
                     printfn "%d" value
                     true
                 else false)

你可以使用的内置函数越多,你的错误就越少。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-25
    • 1970-01-01
    • 2019-12-10
    • 1970-01-01
    • 2015-05-30
    • 1970-01-01
    相关资源
    最近更新 更多