2

F#レコードがある場合:

type R = { X : string ; Y : string }

および2つのオブジェクト:

let  a = { X = null ; Y = "##" }
let  b = { X = "##" ; Y = null }

および文字列の述語:

let (!?) : string -> bool = String.IsNullOrWhiteSpace

および関数:

let (-?>) : string -> string -> string = fun x y -> if !? x then y else x

F#引用符を使用して以下を定義する方法はありますか?

let (><) : R -> R -> R

行動を伴う:

let c = a >< b // = { X = a.X -?> b.X ; Y = a.Y -?> b.Y }

だけ(><)でなく、任意のF#レコードタイプでも機能するようになっています。 R

短い(><):任意のレコードタイプと(-?>)そのフィールドに適用可能な補完関数を指定して、引用符を使用してオンザフライでの定義用のF#コードを生成できますか?

引用符を使用できない場合、何ができますか?

4

1 に答える 1

6

F#クォーテーションを使用して特定のレコードごとに関数を作成し、F#PowerPackで使用可能なクォーテーションコンパイラを使用してコンパイルできます。ただし、コメントで述べたように、F#リフレクションを使用する方が間違いなく簡単です。

open Microsoft.FSharp.Reflection

let applyOnFields (recd1:'T) (recd2:'T) f =  
  let flds1 = FSharpValue.GetRecordFields(recd1)  
  let flds2 = FSharpValue.GetRecordFields(recd2)  
  let flds = Array.zip flds1 flds2 |> Array.map f
  FSharpValue.MakeRecord(typeof<'T>, flds)

この関数は、レコードを取得し、それらのフィールドを動的に取得してfから、フィールドに適用します。これを使用して、次のように演算子を実装できます(代わりに、読み取り可能な名前の関数を使用しています)。

type R = { X : string ; Y : string } 
let  a = { X = null ; Y = "##" } 
let  b = { X = "##" ; Y = null } 

let selectNotNull (x:obj, y) =
  if String.IsNullOrWhiteSpace (unbox x) then y else x

let c = applyOnFields a b selectNotNull 

Reflectionを使用したソリューションは非常に簡単に記述できますが、効率が低下する可能性があります。applyOnFields関数が呼び出されるたびに.NETReflectionを実行する必要があります。レコードタイプがわかっている場合は、引用符を使用して、手動で記述できる関数を表すASTを作成できます。何かのようなもの:

let applyOnFields (a:R) (b:R) f = { X = f (a.X, b.X); Y = f (a.Y, b.Y) }

引用符を使用して関数を生成するのはより難しいので、完全なサンプルを投稿することはしませんが、次の例はその少なくとも一部を示しています。

open Microsoft.FSharp.Quotations

// Get information about fields
let flds = FSharpType.GetRecordFields(typeof<R>) |> List.ofSeq

// Generate two variables to represent the arguments
let aVar = Var.Global("a", typeof<R>)
let bVar = Var.Global("b", typeof<R>)

// For all fields, we want to generate 'f (a.Field, b.Field)` expression
let args = flds |> List.map (fun fld ->
  // Create tuple to be used as an argument of 'f'
  let arg = Expr.NewTuple [ Expr.PropertyGet(Expr.Var(aVar), fld)
                            Expr.PropertyGet(Expr.Var(bVar), fld) ]
  // Call the function 'f' (which needs to be passed as an input somehow)
  Expr.App(???, args)

// Create an expression that builds new record
let body = Expr.NewRecord(typeof<R>, args)

適切な見積もりを作成したら、F#PowerPackを使用してコンパイルできます。たとえば、このスニペットを参照してください。

于 2012-02-12T14:09:25.153 に答える