F#互递归实现家族树查询函数时出现FS0001类型不匹配问题
错误成因拆解
- 类型不匹配的核心根源:F#的静态类型推导机制会根据函数所有match分支的返回值统一推导函数的返回类型。你定义的互递归函数
findC在空列表分支|[] -> []直接返回了空列表,推导器会强制将findC的返回类型认定为泛型列表'a list。但find函数在当前节点不匹配时,是要将findC的返回值直接作为自身返回值的——而find的目标返回值是Name * Sex * YearOfBirth * Name list类型的元组,加上findC在匹配到子女节点时返回的是find输出的元组类型,多个分支的返回值无法统一,就直接触发了FS0001类型不匹配报错。 - 附带的逻辑漏洞:原实现的
findC在判断当前子女节点姓名不匹配时,直接跳过该节点进入下一个同级子女,完全没有递归进入当前节点的子树查找深层嵌套的匹配项,哪怕类型问题修复,也无法查找到嵌套层数超过2的成员。
修复后的可运行实现
统一两个互递归函数的返回值类型,补全深层递归查找的逻辑:
type Name = string type Sex = | M // 男性 | F // 女性 type YearOfBirth = int type FamilyTree = P of Name * Sex * YearOfBirth * Children and Children = FamilyTree list // 测试样本 let f1 = P("Larry",M,1920,[P("May",F,1945,[P("Fred",M,1970,[])]);P("Joe",M,1950,[P("Adam",M,1970,[])]);P("Paul",M,1955,[])]) // 互递归主查找函数 let rec find (targetName: Name) (tree: FamilyTree) = match tree with | P(currentName, sex, birthYear, children) -> if currentName = targetName then // 命中节点时返回要求结构的元组,提取所有子女的姓名 (currentName, sex, birthYear, children |> List.map (fun (P(childName, _, _, _)) -> childName)) else // 当前节点未命中,进入子女列表查找 findChildren targetName children // 互递归子女列表遍历函数,返回类型与find完全统一 and findChildren (targetName: Name) (siblings: Children) = match siblings with | [] -> failwith $"未找到姓名为 {targetName} 的家族成员" | currentSibling :: restSiblings -> // 在当前同辈节点的整棵子树中查找 try find targetName currentSibling // 当前同辈节点子树未命中,继续查找剩余同辈节点 with _ -> findChildren targetName restSiblings
验证效果
执行find "May" f1将返回完全符合要求的结果:
("May", F, 1945, ["Fred"])
如果需要适配找不到目标的场景,也可以将两个互递归函数的返回值统一包裹成Option<Name * Sex * YearOfBirth * Name list>类型,替代原来的异常抛出逻辑。
内容的提问来源于stack exchange,提问作者weblasky
相关产品推荐
相关产品推荐

