F#模式与可选的元组列表匹配

问题描述 投票:1回答:2

我正在尝试将模式匹配用于元组的可选列表,但是尽管尝试了所有我能想到的东西,但我无法编写详尽的匹配表达式。

我正在努力理解为什么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# pattern-matching
2个回答
0
投票

很好的问题!我认为许多以F#开始的人都在努力处理列表,选项和元组的交互方式。首先,我要说:编译器是正确的。简短的答案是:您只匹配单例列表。让我尝试更深入地解释一下。

您的类型基本上是('a * 'b) list option。在您的情况下,'a'b本身是使用字符串区分的一种情况。让我们简化一下,看看如果我们单独查看类型的每个部分会发生什么(您可能已经知道这一点,但是将其放在上下文中可能会有所帮助):

  1. 首先,您的类型为选项。它有两个值NoneSome 'a。要匹配一个选项,您可以执行[

    match o with 
    | Some value -> value 
    | None -> failwith "nothing"`
    
  2. 接下来,您的类型是列表。列表中的项目用分号;分隔。一个空列表是[],一个单例列表(一个带有一个项目)是[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
    
  3. 第三,您的列表类型本身就是一个元组类型。要对元组类型进行解构或匹配,可以像,一样使用逗号match (x, y) with 1, 2 -> "it's 1 and 2!" ...

  4. 合并所有这些,我们必须在选项(外部),列表(中间),元组上进行匹配。类似Some []表示一个空列表,None表示没有列表,Some [a, b]表示单例列表,Some (a,b)::rest表示具有一个或多个项目的列表。


现在我们已经不了解理论了,让我们看看我们是否可以解决您的代码。首先让我们看看警告消息:

此表达式上的不完整模式匹配。 Some ([_;_])可能表示情况...

[这是正确的,代码中的项目用,分隔,表示元组,并且消息显示Some [something; something](下划线表示“任何内容”),这是两个项目的列表。但这对您添加它没有多大帮助,因为列表仍然可以大于2。

骑手说这条规则永远不会被匹配

车手是正确的(在下面调用FSC编译器服务)。该行上方的规则是Some [(x:A,y:B)](此处不需要:A:B),它匹配任何Some带元组的单例数组Some [_,_]的作用相同,只是它不捕获变量中的值。

这消除了警告,但是前两个没有涵盖什么情况?

它消除了警告,因为Some _表示带有[[anything的Some,因为_只是表示:它是任何内容的占位符。在这种情况下,它匹配空列表,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

(您(可能)需要的是这里:

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就足够了。这将使匹配更加容易。


0
投票
您正在努力,因为您的榜样太过“榜样”。

让我们将您的示例转换为更有意义的示例:检查输入,以便如此

    如果为空,则不打印任何内容,否则:
  • 如果元素为零,则打印“空”
  • 如果只有一个元素,则打印“一个元素:...”
  • 如果有两个元素,则打印“我们有两个元素:...”
  • 如果有三个元素,则打印“有三个元素:...”
  • 如果它包含三个以上元素,则打印“哦,老兄,第一个元素是...,第二个元素是...,第三个元素是...,还有N个元素更多”]]
  • 现在您可以看到您的代码仅涵盖前3种情况。因此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#时,直到在生产代码中使用它们时,我才了解许多功能/语法。

© www.soinside.com 2019 - 2024. All rights reserved.