如何处理不同类型的算法输入?
How to handle different types for input of algorithm?
我有一个算法可以根据用户输入的内容查找数据。有多种方法可以指定 unique 记录,因此我允许用户输入多个不同的唯一标识符。然而,当我开始编写算法时,我的脑海中响起了警钟,因为它看起来很冗长或不起作用。感觉就像我做错了什么。让我用代码告诉你。
// 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))
不知何故,需要打开所有东西对我来说似乎太过分了。在这种情况下我不应该使用受歧视的联盟吗?是否有我可以利用的既定功能设计模式?
您当然可以包装相同的 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
对于另一种解决方案,我会将员工信息保存在一个记录中。并且始终 return 值作为完整记录,然后提取必要的信息。对于 Key,您可以为记录的不同部分构建各种地图。如果更好的话,您甚至可以嵌套地图。
我有一个算法可以根据用户输入的内容查找数据。有多种方法可以指定 unique 记录,因此我允许用户输入多个不同的唯一标识符。然而,当我开始编写算法时,我的脑海中响起了警钟,因为它看起来很冗长或不起作用。感觉就像我做错了什么。让我用代码告诉你。
// 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))
不知何故,需要打开所有东西对我来说似乎太过分了。在这种情况下我不应该使用受歧视的联盟吗?是否有我可以利用的既定功能设计模式?
您当然可以包装相同的 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
对于另一种解决方案,我会将员工信息保存在一个记录中。并且始终 return 值作为完整记录,然后提取必要的信息。对于 Key,您可以为记录的不同部分构建各种地图。如果更好的话,您甚至可以嵌套地图。