【问题标题】:F# branching logic with pattern matching具有模式匹配的 F# 分支逻辑
【发布时间】:2019-05-21 13:49:55
【问题描述】:

让我在 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
    ...

【问题讨论】:

    标签: f#


    【解决方案1】:

    我认为这已经足够了:

    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# 类型系统的强大功能及其简洁的语法,我建议您更进一步,编写一个新的字符串类型,确保其内容不为空也不为空。这样每个人在使用该类型的值时都会感到安全。

    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
        | _ -> []
    

    【讨论】:

      【解决方案2】:

      可以进一步重构 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-10-24
        • 1970-01-01
        • 2016-02-06
        • 2018-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-17
        相关资源
        最近更新 更多