3

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

// 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
4

3 回答 3

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())
于 2012-09-26T21:37:53.123 回答
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

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

于 2012-09-26T22:24:05.303 回答
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
}

由于您将相同的参数传递给每个函数,因此 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 必须是元组形式。

于 2012-09-26T21:50:31.593 回答