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)

アップデート:

この手法は、クロージャを使用するように簡単に拡張できるため、引数が異なる関数(funsも多数あります:-))で機能します。

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

I think the cleanest option is to define the getIndexByXYZ operations as active patterns rather than as functions. Then you can write the following pattern matching:

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

If you still want to use the functions in other parts of your program in a context where you're not pattern matching on them, then you can define active patterns as simple wrappers:

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

Sadly, there is no way to turn the functions into active patterns "automatically" but I think it is well worth the additional effort if you need to express some business logic and want it to be readable.

于 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
}

すべての関数に同じ引数を渡しているため、パッドのソリューションは可能な限り簡潔になる可能性があります。これらのソリューションは、入れ子になった を置き換える一般的な問題に対処します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

これには、関数を少し変更する必要があります。引数はタプル形式でなければなりません。

于 2012-09-26T21:50:31.593 に答える