我有一个根据用户输入的内容查找数据的算法。有多种方法可以指定唯一记录,因此我允许用户输入多个不同的唯一标识符。然而,当我开始编写算法时,我的头脑中响起了警钟,因为它看起来很冗长或不起作用。这只是感觉我做错了什么。让我用代码告诉你。
// Types for the domain model
type EmployeeID = ID of int
type EmployeeName =
{ First : string
Last : string }
// Some instances of EmployeeName to use later
let james = {First = "James"; Last = "Winklenaught"}
let ted = {First = "Theodore"; Last = "Chesterton"}
// Input for the algorithm
type matcherInput =
| EmployeeIDWrapper of EmployeeID
| EmployeeNameWrapper of EmployeeName
// Output of the algorithm
type matcherOutput =
{ Info : string }
// Returns data if it found it from our search algorithm
let LookupEmployee (input : matcherInput) : matcherOutput option =
(*
There's a lot of algorithm here in the real version
that creates the lookup tables (maps). I just put in
some dummy data instead.
*)
let numberLookup =
Map.ofList [(james, ID 1); (ted, ID 2)]
let infoLookup =
Map.ofList [(ID 1,{Info = "CEO"});(ID 2,{Info = "CFO"})]
// output
match input with
| EmployeeIDWrapper number ->
Map.tryFind number infoLookup
| EmployeeNameWrapper name ->
Map.tryFind name numberLookup
|> Option.bind (fun number -> Map.tryFind number infoLookup)
// doesn't work = (
LookupEmployee james
LookupEmployee (ID 1)
// right, but verbose
LookupEmployee (EmployeeNameWrapper james)
LookupEmployee (EmployeeIDWrapper (ID 1))
不知何故需要解开一切似乎对我来说太过分了。在这种情况下我不应该使用受歧视的工会吗?是否有我可以利用的既定功能设计模式?
答案 0 :(得分:3)
您当然可以包装相同的DU案例,但是您需要调用MatcherInput.EmployeeID 2来获取MatcherOutput.EmployeeID。如有必要,您可以使用一些活动模式隐藏魔法。另一件事是,我假设记录应该包含在Name中。
type EmployeeName =
{ First : string
Last : string }
type MatcherInput =
| Name of EmployeeName
| EmployeeID of int
| Info of string
let james = Name {First = "James"; Last = "Winklenaught"}
let ted = Name {First = "Theodore"; Last = "Chesterton"}
let LookupEmployee (input: MatcherInput) =
let numberLookup =
Map.ofList [(james, EmployeeID 1); (ted, EmployeeID 2)]
let infoLookup =
Map.ofList [(EmployeeID 1,Info "CEO");(EmployeeID 2,Info "CFO")]
match input with
| Name n -> numberLookup.[Name n]
| EmployeeID _ -> infoLookup.[input]
| Info _ -> failwith "Dont match on Info"
LookupEmployee ted
LookupEmployee (EmployeeID 2)
如果您希望将输入和输出类型分开,并且将在更多类型上进行匹配,您可以使用泛型DU:
type EmployeeName =
{ First : string
Last : string }
type MatcherInput =
| Name of EmployeeName
| EmployeeID of int
type MatcherOutput<'a> =
| Other of 'a
| Info of string
let james = Name {First = "James"; Last = "Winklenaught"}
let ted = Name {First = "Theodore"; Last = "Chesterton"}
let LookupEmployee (input: MatcherInput) =
let numberLookup =
Map.ofList [(james, EmployeeID 1); (ted, EmployeeID 2)]
let infoLookup =
Map.ofList [(EmployeeID 1,Info "CEO");(EmployeeID 2,Info "CFO")]
match input with
| Name _ -> Other (numberLookup.[input])
| EmployeeID _ -> infoLookup.[input]
let x = EmployeeID 1
LookupEmployee ted
LookupEmployee x
对于另一种解决方案,我会将员工信息保存在一条记录中。并始终返回Value作为完整记录,然后提取必要的信息。对于Key,您可以为记录的不同部分构建各种映射。如果更好的话,你甚至可以嵌套地图。