F# 列表模式匹配限制还是写得不好?
F# list pattern matching limitations or just bad writing?
给定一些 CreditScoreInput
:
type CreditScoreInput = { id: string; score: string; years: int }
let input = [
{ id = "CUSTOMER001"; score = "Medium"; years = 1 }
{ id = "CUSTOMER001"; score = "Medium"; years = 1 }
{ id = "CUSTOMER002"; score = "Medium"; years = 10 }
{ id = "CUSTOMER003"; score = "Bad"; years = 0 }
{ id = "CUSTOMER003"; score = "Bad"; years = 0 }
{ id = "CUSTOMER003"; score = "Bad"; years = 0 }
{ id = "CUSTOMER004"; score = "Good"; years = 0 }
{ id = "CUSTOMER005"; score = "Good"; years = 10 }
]
我的函数 validateDuplicates
正在寻找重复项:
let validate list =
match list with
| [] -> failwith "No customers supplied!"
| _ -> list
let validateDuplicates (group:string * list<CreditScoreInput>) =
match group with
| (id, g) when g.Length = 1 -> printf $"No duplicates for {id} is OK.\n"
| (id, [input1; input2]) -> printf $"Two duplicates for {id} is OK.\n"
| (id, g) when g.Length > 2 -> printf $"More than two duplicates for {id} is NOT OK.\n"
true
input
|> validate
|> List.groupBy (fun i -> i.id)
|> List.forall (fun i -> i |> validateDuplicates)
|> ignore
在 validateDuplicates
内部,我注意到 group
下有一点波浪形,导致出现警告:
Incomplete pattern matches on this expression. For example, the value (_,[_;_;_])
may indicate a case not covered by the pattern(s). However, a pattern rule with a when
clause might successfully match this value.
有什么方法可以很好地使用编译器来避免此警告?
更新
我不确定我是否应该在这里这样做,但这里是我根据出色的指导所做的更改:
let validateDuplicates (group:string * list<CreditScoreInput>) =
match group with
| (id, [_]) -> printf $"No duplicates for {id} is OK.\n"
| (id, [_; _]) -> printf $"Two duplicates for {id} is OK.\n"
| (id, g) -> printf $"More than two duplicates for {id} is NOT OK.\n"
input
|> validate
|> List.groupBy (fun i -> i.id)
|> List.iter (fun i -> i |> validateDuplicates)
只需删除最后一个 when
子句,因为您知道它永远为真:
match group with
| (id, g) when g.Length = 1 -> printf $"No duplicates for {id} is OK.\n"
| (id, [input1; input2]) -> printf $"Two duplicates for {id} is OK.\n"
| (id, g) -> printf $"More than two duplicates for {id} is NOT OK.\n"
证明:
g.Length
永远不能为负数或 0
- 如果
g.Length
为1则匹配第一种情况
- 如果
g.Length
是 2 那么它将匹配第二种情况
- 因此,如果控制达到第三种情况,
g.Length
将始终> 2。
以下是我建议您改为编写此代码的方式:
let validateDuplicates (id, g : List<_>) =
match g.Length with
| 0 -> failwith "Unexpected"
| 1 -> printf $"No duplicates for {id} is OK.\n"
| 2 -> printf $"Two duplicates for {id} is OK.\n"
| _ -> printf $"More than two duplicates for {id} is NOT OK.\n"
input
|> validate
|> List.groupBy (fun i -> i.id)
|> List.iter validateDuplicates
我所做的更改是:
- 使用
List.iter
而不是管道 List.forall
进入 ignore
。
- 消除调用
validateDuplicates
时不需要的 lambda。
- 使用模式匹配解构输入
validateDuplicates
- 直接匹配
g.Length
而不是使用 when
子句。
- 防御性编程:显式检查空列表以明确您的意图。
您可能还想考虑通过 F# 的 Result
类型使您的验证函数纯(即没有副作用)。
给定一些 CreditScoreInput
:
type CreditScoreInput = { id: string; score: string; years: int }
let input = [
{ id = "CUSTOMER001"; score = "Medium"; years = 1 }
{ id = "CUSTOMER001"; score = "Medium"; years = 1 }
{ id = "CUSTOMER002"; score = "Medium"; years = 10 }
{ id = "CUSTOMER003"; score = "Bad"; years = 0 }
{ id = "CUSTOMER003"; score = "Bad"; years = 0 }
{ id = "CUSTOMER003"; score = "Bad"; years = 0 }
{ id = "CUSTOMER004"; score = "Good"; years = 0 }
{ id = "CUSTOMER005"; score = "Good"; years = 10 }
]
我的函数 validateDuplicates
正在寻找重复项:
let validate list =
match list with
| [] -> failwith "No customers supplied!"
| _ -> list
let validateDuplicates (group:string * list<CreditScoreInput>) =
match group with
| (id, g) when g.Length = 1 -> printf $"No duplicates for {id} is OK.\n"
| (id, [input1; input2]) -> printf $"Two duplicates for {id} is OK.\n"
| (id, g) when g.Length > 2 -> printf $"More than two duplicates for {id} is NOT OK.\n"
true
input
|> validate
|> List.groupBy (fun i -> i.id)
|> List.forall (fun i -> i |> validateDuplicates)
|> ignore
在 validateDuplicates
内部,我注意到 group
下有一点波浪形,导致出现警告:
Incomplete pattern matches on this expression. For example, the value
(_,[_;_;_])
may indicate a case not covered by the pattern(s). However, a pattern rule with awhen
clause might successfully match this value.
有什么方法可以很好地使用编译器来避免此警告?
更新
我不确定我是否应该在这里这样做,但这里是我根据出色的指导所做的更改:
let validateDuplicates (group:string * list<CreditScoreInput>) =
match group with
| (id, [_]) -> printf $"No duplicates for {id} is OK.\n"
| (id, [_; _]) -> printf $"Two duplicates for {id} is OK.\n"
| (id, g) -> printf $"More than two duplicates for {id} is NOT OK.\n"
input
|> validate
|> List.groupBy (fun i -> i.id)
|> List.iter (fun i -> i |> validateDuplicates)
只需删除最后一个 when
子句,因为您知道它永远为真:
match group with
| (id, g) when g.Length = 1 -> printf $"No duplicates for {id} is OK.\n"
| (id, [input1; input2]) -> printf $"Two duplicates for {id} is OK.\n"
| (id, g) -> printf $"More than two duplicates for {id} is NOT OK.\n"
证明:
g.Length
永远不能为负数或 0- 如果
g.Length
为1则匹配第一种情况 - 如果
g.Length
是 2 那么它将匹配第二种情况 - 因此,如果控制达到第三种情况,
g.Length
将始终> 2。
以下是我建议您改为编写此代码的方式:
let validateDuplicates (id, g : List<_>) =
match g.Length with
| 0 -> failwith "Unexpected"
| 1 -> printf $"No duplicates for {id} is OK.\n"
| 2 -> printf $"Two duplicates for {id} is OK.\n"
| _ -> printf $"More than two duplicates for {id} is NOT OK.\n"
input
|> validate
|> List.groupBy (fun i -> i.id)
|> List.iter validateDuplicates
我所做的更改是:
- 使用
List.iter
而不是管道List.forall
进入ignore
。 - 消除调用
validateDuplicates
时不需要的 lambda。 - 使用模式匹配解构输入
validateDuplicates
- 直接匹配
g.Length
而不是使用when
子句。 - 防御性编程:显式检查空列表以明确您的意图。
您可能还想考虑通过 F# 的 Result
类型使您的验证函数纯(即没有副作用)。