Gru*_*uez 2 f# pattern-matching
我正在尝试对可选的元组列表使用模式匹配,但尽管尝试了我能想到的一切,但我无法编写详尽的匹配表达式。
我很难理解为什么 F# 编译器坚持认为我在以下示例中的模式并不详尽。
module Mapper.PatternMatchingOddity
type A = A of string
type B = B of string
type ProblemType = ProblemType of (A * B) list option
//Incomplete pattern matches on this expression. Some ([_;_]) may indicate a case...
let matchProblem = function
|Some [(x:A,y:B)] -> []
|Some ([_,_]) -> [] //rider says this rule will never be matched
|None -> []
//same as before
let matchProblem1 = function
|Some [_,_] -> []
|Some [] -> []
//|Some _ -> []//this removes the warning but what is the case not covered by the previous two?
|None -> []
let matchProblem2 (input:ProblemType) =
match input with //same as before
|ProblemType (Some [(x:A,y:B)]) -> []
|ProblemType None -> []
Run Code Online (Sandbox Code Playgroud)
我如何编写详尽的匹配以及我上面遗漏了什么?您能否给出一个输入示例,该输入将被接受为这些函数的有效参数并跳过模式?
好问题!我认为许多刚开始使用 F# 的人都在努力解决列表、选项和元组如何交互。首先我要说的是:编译器是正确的。简短的答案是:您仅匹配单例列表。让我尝试更深入地解释一下。
本质上,你的类型是('a * 'b) list option。在您的情况下,'a和'b本身就是字符串的单例区分使用。让我们稍微简化一下,看看如果我们单独查看类型的每个部分会发生什么(您可能已经知道这一点,但将其放在上下文中可能会有所帮助):
首先,你的类型是选项。它有两个值,None或Some 'a。要匹配一个选项,你可以这样做
match o with
| Some value -> value
| None -> failwith "nothing"`
Run Code Online (Sandbox Code Playgroud)接下来,您的类型是列表。列表中的项目用分号分隔;。空列表是[],单例列表(包含单个项目)是[x]和多个项目[x;y...]。要将某些内容添加到列表的开头,请使用::. 列表是一种特殊类型的可区分联合,匹配它们的语法模仿列表构造的语法:
match myList with
| [] -> "empty"
| [x] -> printfn "one item: %A" x
| [x; y] -> printfn "two items: %A, %A" x y
| x::rest -> printfn "more items, first one: %A" x
Run Code Online (Sandbox Code Playgroud)第三,您的列表类型本身就是元组类型。要解构或匹配元组类型,您可以使用逗号,,如match (x, y) with 1, 2 -> "it's 1 and 2!" ...。
结合所有这些,我们必须匹配一个选项(外部),然后列表(中间),然后元组。类似于Some []空列表、None缺少列表、Some [a, b]单例列表以及Some (a,b)::rest包含一个或多个项目的列表。
现在我们已经掌握了理论,让我们看看是否可以解决您的代码。首先我们看一下警告信息:
该表达式的模式匹配不完整。
Some ([_;_])可能表明一个案例...
这是正确的,代码中的项目通过,表示元组来分隔,并且消息显示Some [something; something](下划线表示“任何内容”),这是两个项目的列表。但添加它对你没有多大帮助,因为列表仍然可能比 2 长。
骑手说这条规则永远不会被匹配
Rider 是正确的(它调用下面的 FSC 编译器服务)。该行上方的规则是Some [(x:A,y:B)](此处不需要:A和),它与任何带有 tuple 的单例数组匹配。执行相同的操作,只是它不捕获变量中的值。:BSomeSome [_,_]
这消除了警告,但是前两者未涵盖的情况是什么?
它删除了警告,因为Some _意味着任何东西Some,as仅仅意味着:它是任何东西的占位符。在这种情况下,它匹配空列表、2 项列表、3 项列表、n 项列表(唯一匹配的是该示例中的 1 项列表)。_
您能否举一个被接受为有效参数的输入示例
是的。您不匹配的有效输入是Some [](空列表)、Some [A "a", B "x"; A "2", B "2"](两个项目的列表)等。
让我们举第一个例子。你有这个:
let matchProblem = function
|Some [(x:A,y:B)] -> [] // matching a singleton list
|Some ([_,_]) -> [] // matches a singleton list (will never match, see before)
|None -> [] // matches None
Run Code Online (Sandbox Code Playgroud)
这就是您(可能)需要的:
let notAProblemAnymore = function
// first match all the 'Some' matches:
| Some [] -> "empty" // an empty list
| Some [x,y] -> "singleton" // a list with one item that is a tuple
| Some [_,a;_,b] -> "2-item list" // a list with two tuples, ignoring the first half of each tuple
| Some ((x,y)::rest) -> "multi-item list"
// a list with at least one item, and 'rest' as the
// remaining list, which can be empty (but won't,
// here it has at least three items because of the previous matches)
| None -> "Not a list at all" // matching 'None' for absence of a list
Run Code Online (Sandbox Code Playgroud)
总结一下:您正在匹配一个只有一项的列表,编译器抱怨您错过了其他长度的列表(空列表和具有多个项目的列表)。
通常不需要option与列表一起使用,因为空列表已经意味着没有数据。因此,每当您发现自己正在编写这种类型时,option list请考虑一下是否list就足够了。这将使匹配变得更容易。