具有模式匹配功能的F#分支逻辑

时间:2019-05-21 13:49:55

标签: f#

让我在C#上有这样的代码。如何用F#在函数样式中重写此早午餐逻辑?我应该使用什么模式匹配?主动模式匹配?区分工会?

public class DataBase
{
    public List<string> GetEmployees(string id, string email)
    {
        if (!string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(email))
        {
            return GetByIdAndEmail(id, email);
        }
        else if (!string.IsNullOrEmpty(email))
        {
            return GetByEmail(email);
        }
        else if (!string.IsNullOrEmpty(id))
        {
            return GetById(id);
        }
        else
        {
            return new List<string>();
        }
    }

    private List<string> GetByIdAndEmail(string id, string email)
    {
        // request something in db
        return new List<string>() { "First" };
    }

    private List<string> GetByEmail(string email)
    {
        //request something in db
        return new List<string>() { "Second" };
    }

    private List<string> GetById(string id)
    {
        // request something in db
        return new List<string>() { "Third" };
    }
}

class Program
{
    static void Main(string[] args)
    {
        DataBase DB = new DataBase();
        string id = null;
        string email = null;
        DB.GetEmployees(id, email);
    }
}

F#

let GetEmployees (id:string)(email:string) = 
    match (id,email) with
    ...

2 个答案:

答案 0 :(得分:6)

我认为这已经足够了:

let getByIdAndEmail id email = ["First"]
let getByEmail email = ["Second"]
let getById id = ["Third"]

let getEmployees id email = 
    match String.IsNullOrEmpty id, String.IsNullOrEmpty email with
    | false, false -> getByIdAndEmail id email 
    | true, false  -> getByEmail email
    | false, true  -> getById id
    | _ -> []

如果您想利用F#类型系统的功能及其简洁的语法,我建议您进一步编写一种新的字符串类型,以确保其内容不为null或为空。这样,使用该类型的值时,每个人都会感到安全。

type ReallyString = private ReallyString of string with
    static member Create s = match String.IsNullOrEmpty s with
                             | true -> None
                             | false -> Some (ReallyString s)
    member this.Value = let (ReallyString s) = this in s

let getByIdAndEmail (id : ReallyString) (email : ReallyString) =
    // Yay, I feel safe because the id and email really have content
    // I can use id.Value and email.Value without any worry
    ["First"]
let getByEmail (email : ReallyString) =
    // Yay, I feel safe because the email really has content
    // I can use email.Value without any worry
    ["Second"]
let getById (id : ReallyString) =
    // Yay, I feel safe because the id really has content
    // I can use id.Value without any worry
    ["Third"]

let getEmployees id email =
    // I feel unsafe here, so I have to check
    // Actually I'm being forced to check because if I don't check
    // I won't have valid values to pass to the getByXXX functions above
    match ReallyString.Create id, ReallyString.Create email with
    | Some reallyId, Some reallyEmail -> getByIdAndEmail reallyId reallyEmail
    | Some reallyId, None -> getById reallyId
    | None, Some reallyEmail -> getByEmail reallyEmail
    | _ -> []

答案 1 :(得分:1)

可以使用主动模式和区分联合来进一步进行Nghia Buis答案中的重构。

open System.Text.RegularExpressions
let (|EmployeeId|_|) s = 
    match System.Int32.TryParse(s) with   // assuming your id:s are integers
    | (true,int) -> Some(int)
    | _ -> None

let (|Email|_|) s =
    let matches = Regex.Matches(s, ".*?@.*" )
    if matches.Count > 0 then Some matches.[0].Value else None

type IdString = private IdString of string with
    static member Create = 
        function
        | EmployeeId s -> Some s
        | _ -> None
    member this.Value = let (IdString s) = this in s

type Email = private Email of string with
    static member Create = 
        function
        | Email s -> Some s
        | _ -> None
    member this.Value = let (Email s) = this in s

let getByIdAndEmail (id : EmployeeId) (email : Email) =
    // Yay, I know that id and email are valid, non empty and not null
    ["First"]

let getByEmail (email : Email) =
    // email is an email adress, and not any string
    ["Second"]
let getById (id : EmployeeId) =
    // id is really an employeeId.
    ["Third"]

let getEmployees id email =
    // I feel unsafe here, so I have to check
    // Actually I'm being forced to check because if I don't check
    // I won't have valid values to pass to the getByXXX functions above
    match EmployeeId.Create id, Email.Create email with
    | Some reallyId, Some reallyEmail -> getByIdAndEmail reallyId reallyEmail
    | Some reallyId, None -> getById reallyId
    | None, Some reallyEmail -> getByEmail reallyEmail
    | _ -> []

您可能还想在代码中明确输入如何识别员工。

type EmployeeIdentification =  //Let's state explicitly how employees may be identified
    | ById of EmployeeId
    | ByEmail of Email
    | ByIdAndEmail of EmployeeId * Email