如何确定两种类型之间的单个属性差异?

时间:2019-02-14 20:07:22

标签: f# comparison

我一直在寻找一种更简洁的编码方式(如下)。基本上,我需要一种方法来确定一组属性中只有一个不等于其他类型(addressalternateAddress是不同类型)上的对应属性。如果5个值中只有一个不匹配,则我要特定的错误,否则我要更一般的错误。

请注意,==!=是自定义中缀运算符,用于不区分大小写的比较。

   open System

   type Errors =
   | InvalidStreet
   | InvalidCity
   | InvalidState
   | InvalidPostalCode
   | InvalidCountry
   | InvalidAddress

   type Address =
       {
           Street: string
           City: string
           Region: string
           PostalCode: string
           Country: string
       }

   type AlternateAddress =
       {
           Street: string
           City: string
           Region: string
           PostalCode: string
           Country: string
       }

   let inline (==) (s1: string) (s2: string) = s1.Equals(s2, StringComparison.CurrentCultureIgnoreCase)
   let inline (!=) s1 s2 = s1 == s2 |> not

   let address = {Address.Street = "123 Main St."; City = "Happytown"; Region = "CA"; PostalCode = "90210"; Country = "USA"}
   let alternateAddress = Some {AlternateAddress.Street = "123 Main"; City = "Happytown"; Region = "CA"; PostalCode = "90210"; Country = "USA"}

   match alternateAddress with
   | Some alternateAddress ->
       if
              address.Street      != alternateAddress.Street
           && address.City        == alternateAddress.City
           && address.Region      == alternateAddress.Region
           && address.PostalCode  == alternateAddress.PostalCode
           && address.Country     == alternateAddress.Country
       then InvalidStreet
       elif
              address.Street      == alternateAddress.Street
           && address.City        != alternateAddress.City
           && address.Region      == alternateAddress.Region
           && address.PostalCode  == alternateAddress.PostalCode
           && address.Country     == alternateAddress.Country
       then InvalidCity
       elif
              address.Street       == alternateAddress.Street
           && address.City         == alternateAddress.City
           && address.Region       != alternateAddress.Region
           && address.PostalCode   == alternateAddress.PostalCode
           && address.Country      == alternateAddress.Country
       then InvalidState
       elif
              address.Street       == alternateAddress.Street
           && address.City         == alternateAddress.City
           && address.Region       == alternateAddress.Region
           && address.PostalCode   != alternateAddress.PostalCode
           && address.Country      == alternateAddress.Country
       then InvalidPostalCode
       elif
              address.Street       == alternateAddress.Street
           && address.City         == alternateAddress.City
           && address.Region       == alternateAddress.Region
           && address.PostalCode   == alternateAddress.PostalCode
           && address.Country      != alternateAddress.Country
       then InvalidCountry
       else InvalidAddress
   | _ -> InvalidAddress

1 个答案:

答案 0 :(得分:4)

执行此操作的一种方法是为每个字段定义一个对应的“获取器”列表(获取一条记录并返回字符串的函数)以及相关的错误。为简单起见,我将alternateAddress设为非可选:

let comparers : ((Address -> _) * (AlternateAddress -> _) * _) list = 
  [ (fun x -> x.Street), (fun x -> x.Street), InvalidStreet;
    (fun x -> x.City), (fun x -> x.City), InvalidStreet 
    (fun x -> x.Region), (fun x -> x.Region), InvalidStreet 
    (fun x -> x.PostalCode), (fun x -> x.PostalCode), InvalidStreet 
    (fun x -> x.Country), (fun x -> x.Country), InvalidStreet ]

现在,您可以遍历comparers并使用第一个函数从Address获取字段,第二个函数从AlternateAddress获取字段,如果它们不匹配,则返回元组的第三个元素,即要报告的错误。

您可以使用List.choose来获取所有字段匹配时为空的列表,否则,当字段不匹配时包含错误列表:

let errors = comparers |> List.choose (fun (getAddr, getAlt, err) ->
  if getAddr address != getAlt alternateAddress then Some err else None)

如果没有问题,errors列表将为空,如果只有一个错误或包含多个错误,则包含一个错误:

match errors with
| [] -> printfn "All good!"
| [err] -> printfn "One error: %A" err
| _ -> printfn "Multiple errors!"

值得注意的是,根据您的具体情况,重组代码以使此操作更容易可能是一个好主意-但很难说,如果不了解您的情况。