我的匹配似乎很笨重,有没有更好的方法来编码?

时间:2012-09-26 21:30:14

标签: f# pattern-matching option

这似乎有效,但看起来很笨重。有没有更好的方法来编码呢?

// Hunting for the best index to use for a data compare
let getIndex connDB strTable =
    match getIndexByPrimaryKey connDB strTable with
    | Some(name) -> Some(name)  
    | None ->
    match getIndexByCluster connDB strTable with
    | Some(name) -> Some(name)
    | None -> 
    match getIndexByFirstColumns connDB strTable with
    | Some(name) -> Some(name)
    | None -> 
    match getIndexByOnlyUnique connDB strTable with
    | Some(name) -> Some(name)
    | None -> 
    match getAnyUniqueIndex connDB strTable with
    | Some(name) -> Some(name)
    | None -> None

3 个答案:

答案 0 :(得分:8)

最简洁的版本可能是使用List.tryPick

let getIndex connDB strTable =
  [ getIndexByPrimaryKey;
    getIndexByCluster;
    getIndexByFirstColumns;
    getIndexByOnlyUnique;
    getAnyUniqueIndex; ]
  |> List.tryPick (fun fn -> fn connDB strTable)

<强>更新

该技术可以很容易地扩展为使用闭包,因此它适用于具有不同参数的函数(还有很多fun s: - )):

let getIndex connDB strTable =
  [ fun () -> getIndexByPrimaryKey connDB strTable;
    fun () -> getIndexByCluster connDB strTable;
    fun () -> getIndexByFirstColumns connDB strTable;
    fun () -> getIndexByOnlyUnique connDB strTable;
    fun () -> getAnyUniqueIndex connDB strTable; ]
  |> List.tryPick (fun fn -> fn())

答案 1 :(得分:3)

我认为最干净的选择是将getIndexByXYZ操作定义为活动模式而不是函数。然后你可以编写以下模式匹配:

let getIndex connDB strTable = 
    match connDB, strTable with
    | IndexByPrimaryKey name 
    | IndexByCluster name 
    | IndexByFirstColumns name 
    | IndexByOnlyUnique name 
    | AnyUniqueIndex name -> Some(name)   

如果你仍想在你没有模式匹配的上下文中使用程序其他部分的函数,那么你可以将活动模式定义为简单的包装器:

let (|IndexByPrimaryKey|_|) (connDB, strTable) =
  getIndexByPrimaryKey connDB strTable

可悲的是,没有办法“自动”将这些功能转换为活动模式,但我认为如果你需要表达一些业务逻辑并希望它具有可读性,那么额外的努力是值得的。

答案 2 :(得分:1)

我会写一个orElse函数。然后你可以这样做:

let orElse f = function
  | None -> f()
  | Some _ as x -> x

let getIndex connDB strTable =
  getIndexByPrimaryKey connDB strTable
  |> orElse (fun () -> getIndexByCluster connDB strTable)
  |> orElse (fun () -> getIndexByFirstColumns connDB strTable)
  |> orElse (fun () -> getIndexByOnlyUnique connDB strTable)
  |> orElse (fun () -> getAnyUniqueIndex connDB strTable)

或者,如果您更喜欢“工作流程”语法(并经常需要这样),请执行以下操作:

module OrElse =

  let bind f = function
    | None -> f()
    | Some x -> Some x

  let combine m1 m2 =
    m1 |> bind (fun () -> m2)

  type OrElseBuilder() =
    member x.Zero() = None
    member x.Return(v) = Some v
    member x.Bind(m, f) = bind f m
    member x.ReturnFrom(m) = m
    member x.Combine(m1, m2) = combine m1 m2
    member x.Delay(f) = f()

  let orElse = OrElseBuilder()

会让你更加坦诚地说明:

open OrElse

orElse {
  return! getIndexByPrimaryKey connDB strTable
  return! getIndexByCluster connDB strTable
  return! getIndexByFirstColumns connDB strTable
  return! getIndexByOnlyUnique connDB strTable
  return! getAnyUniqueIndex connDB strTable
}

由于您将相同的args传递给每个函数,因此pad的解决方案可能尽可能简洁。这些解决方案解决了替换嵌套match x with Some _ as v -> v | None -> ...

的一般问题

修改

扩展Tomas的想法,你可以使用通用的活动模式来“模式化”函数:

let (|FN|_|) f x = f x

然后做:

match connDB, strTable with
| FN getIndexByPrimaryKey name -> Some name
| FN getIndexByCluster name -> Some name
| FN getIndexByFirstColumns name -> Some name
| FN getIndexByOnlyUnique name -> Some name
| args -> getAnyUniqueIndex args

这需要对您的函数稍作修改:args必须是tupled形式。