design-patterns - 如何处理不同类型的算法输入?

标签 design-patterns f# functional-programming discriminated-union

我有一个算法,可以根据用户输入的内容查找数据。有多种方法可以指定唯一记录,因此我允许用户输入多个不同的唯一标识符。然而,当我开始编写算法时,我的脑海中响起了警钟,因为它看起来很冗长或不起作用。只是感觉好像我做错了什么。让我用代码向您展示。

// 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。如有必要,您可以使用一些事件模式隐藏魔法。另一件事是,我认为记录应该包含在名称中。

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

对于另一种解决方案,我会将员工信息保存在一个记录中。并始终返回值作为完整记录,然后提取必要的信息。对于键,您可以为记录的不同部分构建各种 map 。如果更好的话,您甚至可以嵌套 map 。

关于design-patterns - 如何处理不同类型的算法输入?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48292483/

相关文章:

c# - 在C#中使用继承类作为实例List

f# - "Run with F# Interactive..."64 位 Windows 中缺少 .fsx 文件的上下文菜单选项

sql - 为什么 F# 中的 orderBy 不会导致 SQL 中的 ORDER BY?

c++ - 高效的函数式风格(C++11 ok)

javascript - 我如何学习 JavaScript 编码模式

java - 单例方法线程安全

javascript - Javascript 域模型对象的约定

f# - Project Euler #14 尝试因 StackOverflowException 而失败

algorithm - 为什么确定一个函数是否是纯函数很困难?

swift - Monads 和 AOP 相关?