我正在尝试编写一个包含元组和表达式列表的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
答案 0 :(得分:3)
您的match
表达式具有最后一个包罗万象的子句(_
子句),该子句仅使用相同的参数再次调用getVar
。如果when (fst head) = exp
条件失败,则代码进入catch-all子句,因此getVar
用相同的参数再次调用,因此第一个条件将失败,因此代码陷入catch- all子句,因此getVar
再次被调用...这是一个无限循环。
您可能想做的是,如果您的getVar
条件失败,则在列表的 tail 上再次致电when
:
match ls with
|head::tail when (fst head) = exp -> printfn "%d" (snd head)
|head::tail -> getVar tail exp
您还需要考虑如果列表为空(该条件需要与[]
匹配的匹配条件)该怎么办。我将由您自己决定,因为您可以有很多事情想要做一个空列表,并且不清楚要哪个。
答案 1 :(得分:2)
您的比赛必须处理三种情况。
第一次尝试时,您不小心一直在整个列表中搜索而不是仅在尾部搜索,从而导致无休止的递归循环。 在第二次尝试中,您在空情况下创建了一个无限循环。 以下是如何编写递归函数的一个示例。
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"
答案 2 :(得分:0)
您的getVar
函数的签名错误。最后一个参数应该是表达式的字母,而不是整个表达式。调用getVar
函数的代码将遍历表达式,对于每个字符,检查它是否是字母,如果是,则调用getVar
,否则执行其他操作。
在其他答案中清楚地说明了代码被挂起的原因,因此在此不再赘述。但是,作为一种好的做法,除非完全控制情况,否则请勿使用| _ -> ...
。通常,我们应该显式地编写所有匹配条件,以便如果我们错过了某些内容,编译器将向我们发出警告。之后,在了解所有条件后,如果确实表示“其余条件 s ”,则可以使用| _ -> ...
。
您的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)
您可以使用越多的内置功能,您遇到的错误越少。