F# —— Quotation 表达式 (一)

发如果你看过我前面的一篇关于自定义Type Provide的博客的话,或许你还记得在当时我使用了Quotation表达式来定义TP 中的成员函数。那么Quotation表达式具体有什么作用,有什么用途呢,下面我们来瞧瞧。

  1.   由于Quotation表达式在编译时,并不会被编译成IL代码,所以在当前程序运行时,Quotation表达式会被忽略。这是第一种情况。
  2.   你可以利用F#中的Pattern match分析上面的AST,从而生成新的代码。
  3.  正如上面的说得一样,Quotation表达式不会被编译成IL代码,但是它会被编译成一个树型的数据结构。此结构类似于语法树(AST),因此对此语法树可以进 行分析,将其转换成可以在其他平台上运行的代码(如SQL,GPU等等,其中转换为SQL就相当于C# 中的LINQ 功能)

在分析其功能之前,我们先看看Quotation 表达式的两种格式:

         第一种:其格式是这样的:  

let exprInt : Expr<int> = <@ 1 + 1 @>

即用<@ @> 将代码括起来。此时变量exprInt就会是这样的类型——Expr<’T>此 T为括号中的结果值的类型。 注意Quotation 表达式中的代码必须是完整的代码,也就是说必须有返回值,不能有形如 let exprInt : Expr<int> = <@ let x = 1 + 1 @> 这样的。

    在第一种格式的情况下,我们需要使用%+ 变量名的形式来调用表达式外部的变量, 如:    

let x = <@ 2 @>
let exprInt : Expr<int> = <@ (%x : int) + 1 + 1 @>

  第二种:格式如下:  

let exprNoTy : Expr = <@@ 1 + 1 @@>

    用<@@ @@>将代码括起。 相应的,当引用外部变量时需要使用%%+ 变量名来实现:    

let y = <@@ 3 @@>
let exprNoTy : Expr = <@@ (%%y:int) + 1 + 1 @@>

下面我们来看看第二个功能的示例(第一个功能直接不做处理即可。。):

  

namespace QuotationExp
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns

module Quotation =
    let rec analyzeExpr (expr : Microsoft.FSharp.Quotations.Expr) =
        match expr with
        //如果是<@@ 1 @@>则 执行此代码
        | Int32(value) -> printfn " integer : %d" value
        //如果是<@@ “1“ @@>则 执行此代码
        | String(value) -> printfn "string : %s" value
        //如果是<@@ 1.0 @@>则 执行此代码
        | Double(value) -> printfn "double number : %f" value

        //如果是<@@ x @@>则 执行此代码,即 如果是变量就执行此行,如当要分析Lambda 表达式中的参数时 :如分析fun x -> x + 1 中的x
        | Var(var) -> printfn "variable : %A" var
        //如果是<@@ fun x -> x + 1 @@>则 执行此代码
        | Lambda(var,body) -> printfn "lambda expression: %A" body

        //当出现的是你指定的函数的时候执行此行, 注意 特定函数必须放在 Call之前
        |SpecificCall <@ (+) @> (inst,methodInfo,[lArg;rArg]) ->
            printfn "calculate the sum of tow arguments : %A, %A" lArg rArg
            analyzeExpr lArg
            analyzeExpr rArg
        //如果表达式是一个函数的时候 且不是上面特定的函数时 执行此行                       
        | Call(callInst,methodInfo,args) ->
            match callInst with
            | Some(inst) -> printfn "%A invoke the method" inst
            | None -> printfn "this is a static method"
            printfn "the method is : %s" methodInfo.Name
            printf "the args of the method: " 
            args |> List.iter(fun x -> analyzeExpr x)
        
        | _ -> printfn "Something else"

    let expr = <@@ (2 * 3) * 1 @@>
    analyzeExpr(expr)

注意:通过分析AST你可以生成自己想要的新代码,也就是在每个 -> 后面的代码,而且每条情况 -> 后面的代码必须是返回相同类型的值。 代码中的Int32 ,String, Double是系统定义的匹配项,因此可以直接进行匹配。

posted @ 2012-12-04 13:52  ZackZhou  阅读(1331)  评论(2编辑  收藏  举报