F# 模式与可选的元组列表匹配
F# pattern matching with optional list of tuples
我正在尝试对可选的元组列表使用模式匹配,但尽管尝试了我能想到的所有方法,我还是无法编写详尽的匹配表达式。
我很难理解为什么 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 -> []
如何编写详尽的匹配以及我在上面遗漏了什么?您能否举例说明可以接受为这些函数的有效参数并通过模式的输入?
好问题!我认为许多开始使用 F# 的人都在努力解决列表、选项和元组的交互方式。首先让我说:编译器是正确的。简短的回答是:你只匹配单例列表。让我尝试更深入地解释一下。
你的类型本质上是 ('a * 'b) list option
。在您的情况下, 'a
和 'b
本身就是使用字符串区分的单例。让我们稍微简化一下,看看如果我们单独查看您的类型的每个部分会发生什么(您可能已经知道这一点,但将其放在上下文中可能会有所帮助):
首先你的类型是option。这有两个值,None
或 Some 'a
。要匹配一个选项,你可以做类似
的事情
match o with
| Some value -> value
| None -> failwith "nothing"`
接下来,你的类型是列表。列表中的项目以分号分隔 ;
。空列表是 []
,单例列表(只有一个项目)是 [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
第三,你的列表类型本身就是一个元组类型。要解构或匹配元组类型,可以使用逗号 ,
,如 match (x, y) with 1, 2 -> "it's 1 and 2!" ...
.
结合所有这些,我们必须匹配一个选项(外部)然后列表(中间)然后元组。 Some []
表示空列表,None
表示缺少列表,Some [a, b]
表示单例列表,Some (a,b)::rest
表示包含一个或多个项目的列表。
现在我们已经了解了理论,让我们看看是否可以处理您的代码。首先让我们看看警告信息:
Incomplete pattern matches on this expression. Some ([_;_])
may indicate a case...
这是正确的,您的代码中的项目由 ,
分隔,表示元组,并且消息显示 Some [something; something]
(下划线表示 "anything"),这是一个列表两个项目。但是添加它对你没有多大帮助,因为列表仍然可以超过 2.
rider says this rule will never be matched
Rider 是正确的(调用下面的 FSC 编译器服务)。该行上方的规则是 Some [(x:A,y:B)]
(此处不需要 :A
和 :B
),它匹配任何带有元组 Some
的单例数组94=]。 Some [_,_]
做同样的事情,只是它不捕获变量中的值。
this removes the warning but what is the case not covered by the previous two?
它删除了警告,因为 Some _
意味着 Some
和 任何东西 ,因为 _
意味着:它是任何东西的占位符.在这种情况下,它匹配空列表、2 项列表、3 项列表和 n 项列表(在该示例中,您唯一匹配的是 1 项列表)。
Can you give an example for an input that would be accepted as a valid parameter
是的。您未匹配的有效输入是 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
这是您(可能)需要的:
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
总而言之:您正在匹配一个只有一项的列表,编译器抱怨您错过了其他长度的列表(空列表和包含多个项目的列表)。
通常没有必要对列表使用option
,因为空列表已经意味着没有数据。因此,每当您发现自己编写类型 option list
时,请考虑仅 list
是否就足够了。这样匹配起来会更方便
你挣扎是因为你的榜样太“榜样”了
让我们将您的示例转换为更有意义的示例:检查输入,以便
- 如果是none则打印“nothing”,否则:
- 如果它有零元素则打印“empty”
- 如果它只有一个元素,则打印“只有一个元素:...”
- 如果它有两个元素,则打印“我们有两个元素:...”
- 如果它有三个元素,则打印“有三个元素:...”
- 如果它有三个以上的元素然后打印“哦,伙计,第一个元素是...,第二个元素是...,第三个元素是...,还有N个元素”
现在你可以看到你的代码只涵盖了前三种情况。所以 F# 编译器是正确的。
重写代码:
let matchProblem (ProblemType input) =
match input with
| None -> printfn "nothing"
| Some [] -> ...
| Some [(x, y)] -> ...
| Some [(x1, y1); (x2, y2)] -> ...
| Some [(x1, y1); (x2, y2); (x3, y3)] -> ...
| Some (x1, y1) :: (x2, y2) :: (x3, y3) :: rest -> // access rest.Length to print the number of more elements
请注意,我在参数 ProblemType input
上使用模式匹配,以便我可以方便地提取输入。这使得后面的模式更简单。
就我个人而言,当我学习 F# 时,我并不了解很多 features/syntax 直到我在生产代码中使用它们。
我正在尝试对可选的元组列表使用模式匹配,但尽管尝试了我能想到的所有方法,我还是无法编写详尽的匹配表达式。
我很难理解为什么 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 -> []
如何编写详尽的匹配以及我在上面遗漏了什么?您能否举例说明可以接受为这些函数的有效参数并通过模式的输入?
好问题!我认为许多开始使用 F# 的人都在努力解决列表、选项和元组的交互方式。首先让我说:编译器是正确的。简短的回答是:你只匹配单例列表。让我尝试更深入地解释一下。
你的类型本质上是 ('a * 'b) list option
。在您的情况下, 'a
和 'b
本身就是使用字符串区分的单例。让我们稍微简化一下,看看如果我们单独查看您的类型的每个部分会发生什么(您可能已经知道这一点,但将其放在上下文中可能会有所帮助):
首先你的类型是option。这有两个值,
的事情None
或Some 'a
。要匹配一个选项,你可以做类似match o with | Some value -> value | None -> failwith "nothing"`
接下来,你的类型是列表。列表中的项目以分号分隔
;
。空列表是[]
,单例列表(只有一个项目)是[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
第三,你的列表类型本身就是一个元组类型。要解构或匹配元组类型,可以使用逗号
,
,如match (x, y) with 1, 2 -> "it's 1 and 2!" ...
.结合所有这些,我们必须匹配一个选项(外部)然后列表(中间)然后元组。
Some []
表示空列表,None
表示缺少列表,Some [a, b]
表示单例列表,Some (a,b)::rest
表示包含一个或多个项目的列表。
现在我们已经了解了理论,让我们看看是否可以处理您的代码。首先让我们看看警告信息:
Incomplete pattern matches on this expression.
Some ([_;_])
may indicate a case...
这是正确的,您的代码中的项目由 ,
分隔,表示元组,并且消息显示 Some [something; something]
(下划线表示 "anything"),这是一个列表两个项目。但是添加它对你没有多大帮助,因为列表仍然可以超过 2.
rider says this rule will never be matched
Rider 是正确的(调用下面的 FSC 编译器服务)。该行上方的规则是 Some [(x:A,y:B)]
(此处不需要 :A
和 :B
),它匹配任何带有元组 Some
的单例数组94=]。 Some [_,_]
做同样的事情,只是它不捕获变量中的值。
this removes the warning but what is the case not covered by the previous two?
它删除了警告,因为 Some _
意味着 Some
和 任何东西 ,因为 _
意味着:它是任何东西的占位符.在这种情况下,它匹配空列表、2 项列表、3 项列表和 n 项列表(在该示例中,您唯一匹配的是 1 项列表)。
Can you give an example for an input that would be accepted as a valid parameter
是的。您未匹配的有效输入是 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
这是您(可能)需要的:
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
总而言之:您正在匹配一个只有一项的列表,编译器抱怨您错过了其他长度的列表(空列表和包含多个项目的列表)。
通常没有必要对列表使用option
,因为空列表已经意味着没有数据。因此,每当您发现自己编写类型 option list
时,请考虑仅 list
是否就足够了。这样匹配起来会更方便
你挣扎是因为你的榜样太“榜样”了
让我们将您的示例转换为更有意义的示例:检查输入,以便
- 如果是none则打印“nothing”,否则:
- 如果它有零元素则打印“empty”
- 如果它只有一个元素,则打印“只有一个元素:...”
- 如果它有两个元素,则打印“我们有两个元素:...”
- 如果它有三个元素,则打印“有三个元素:...”
- 如果它有三个以上的元素然后打印“哦,伙计,第一个元素是...,第二个元素是...,第三个元素是...,还有N个元素”
现在你可以看到你的代码只涵盖了前三种情况。所以 F# 编译器是正确的。
重写代码:
let matchProblem (ProblemType input) =
match input with
| None -> printfn "nothing"
| Some [] -> ...
| Some [(x, y)] -> ...
| Some [(x1, y1); (x2, y2)] -> ...
| Some [(x1, y1); (x2, y2); (x3, y3)] -> ...
| Some (x1, y1) :: (x2, y2) :: (x3, y3) :: rest -> // access rest.Length to print the number of more elements
请注意,我在参数 ProblemType input
上使用模式匹配,以便我可以方便地提取输入。这使得后面的模式更简单。
就我个人而言,当我学习 F# 时,我并不了解很多 features/syntax 直到我在生产代码中使用它们。