2010-04-29 23 views
6

Có cách nào trong F # làm thế nào để có được một tên của một biến được chuyển vào một chức năng?Làm thế nào để có được một tên của một biến đi vào một hàm như một tham số bằng cách sử dụng F #?

Ví dụ:

let velocity = 5 
let fn v = v.ParentName 
let name = fn velocity // this would return "velocity" as a string 

Cảm ơn bạn trước

EDIT:

Tại sao mã này không hoạt động? Nó được so khớp như giá trị, vì vậy tôi không thể lấy tên "biến".

type Test() = 
    let getName (e:Quotations.Expr) = 
    match e with 
     | Quotations.Patterns.PropertyGet (_, pi, _) -> pi.Name + " property" 
     | Quotations.Patterns.Value(a) -> failwith "Value matched" 
     | _ -> failwith "other matched" 
    member x.plot v = v |> getName |> printfn "%s" 

let o = new Test() 

let display() = 
    let variable = 5. 
    o.plot <@ variable @> 

let runTheCode fn = fn() 

runTheCode display 

Trả lời

11

Đối với hoàn trả lời Marcelo của, có bạn có thể sử dụng ngôn cho công việc này:

open Microsoft.FSharp.Quotations 
open Microsoft.FSharp.Quotations.Patterns 

let velocity = 5 

let fn (e:Expr) = 
    match e with 
    | PropertyGet (e, pi, li) -> pi.Name 
    | _ -> failwith "not a let-bound value" 

let name = fn <@[email protected]> 

printfn "%s" name 

Như bạn có thể thấy trong các mã, F # let-ràng buộc giá trị định nghĩa đầu (chức năng hoặc các biến) được thực hiện như tính chất của một lớp học.

Tôi không thể tìm thấy nữa liên kết hiển thị cách một đoạn mã F # có thể được viết lại theo cách chức năng với C#. Nhìn thấy mã, nó trở nên rõ ràng lý do tại sao bạn cần một mô hình PropertyGet.

Bây giờ, nếu bạn muốn đánh giá biểu thức, bạn sẽ cần phải cài đặt F# powerpack và tham chiếu FSharp.PowerPack.Linq trong dự án của bạn.

Nó cho biết thêm một phương pháp EvalUntyped trên Expr lớp ..

open Microsoft.FSharp.Linq.QuotationEvaluation 

let velocity = 5 

let fn (e:Expr) = 
    match e with 
    | PropertyGet (eo, pi, li) -> pi.Name, e.EvalUntyped 
    | _ -> failwith "not a let-bound value" 

let name, value = fn <@[email protected]> 

printfn "%s %A" name value 

Nếu bạn cần phải làm điều đó cho phương pháp của một ví dụ, đây là cách tôi sẽ làm điều đó:

let velocity = 5 

type Foo() = 
    member this.Bar (x:int) (y:single) = x * x + int y 

let extractCallExprBody expr = 
    let rec aux (l, uexpr) = 
    match uexpr with 
    | Lambda (var, body) -> aux (var::l, body) 
    | _ -> uexpr 
    aux ([], expr) 

let rec fn (e:Expr) = 
    match e with 
    | PropertyGet (e, pi, li) -> pi.Name 
    | Call (e, mi, li) -> mi.Name 
    | x -> extractCallExprBody x |> fn 
    | _ -> failwith "not a valid pattern" 

let name = fn <@[email protected]> 
printfn "%s" name 

let foo = new Foo() 

let methodName = fn <@[email protected]> 
printfn "%s" methodName 

Chỉ cần để quay trở lại đoạn mã hiển thị mức sử dụng EvalUntyped, bạn có thể thêm thông số loại rõ ràng cho Expr và số điện thoại (:?>) nếu bạn muốn/cần giữ mọi thứ an toàn loại:

let fn (e:Expr<´T>) = //using ´ instead of ' to avoid colorization screw-up 
    match e with 
    | PropertyGet (eo, pi, li) -> pi.Name, (e.EvalUntyped() :?> ´T) 
    | _ -> failwith "not a let-bound value" 

let name, value = fn <@[email protected]> //value has type int here 
printfn "%s %d" name value 
+0

Xin chào, cảm ơn bạn rất nhiều! Một câu hỏi bổ sung. Làm thế nào để tôi có được một giá trị trong biểu thức? –

+0

Cảm ơn! Và một câu hỏi cuối cùng :) Nếu fn là một thành viên của một loại (thành viên this.fn = ...), làm thế nào để làm điều đó? –

+0

Chỉ cần tò mò, tại sao bạn cần lấy tên của một giá trị/thành viên? – Stringer

1

Bạn có thể có thể đạt được điều này với mã ngôn:

let name = fn <@ velocity @> 

Chức năng fn sẽ được thông qua một đối tượng Expr, mà nó phải ép Quotations.Var (mà nó sẽ chỉ có nếu bạn vượt qua một biến duy nhất) và trích xuất thành viên cá thể Name.

+0

Xin chào, cảm ơn câu trả lời của bạn. Làm thế nào để cast nó? điều này: cho phép velExpr = <@ velocity @> let casted = velExpr:?> Báo giá.Var không hoạt động –

0

Dựa trên các giải pháp trước tôi đi ra ngoài với một giải pháp chung chung hơn, nơi bạn có thể lấy tên của các chức năng, lambdas, các giá trị, tài sản, phương pháp, phương pháp tĩnh, các lĩnh vực công cộng, các loại Union:

open Microsoft.FSharp.Quotations 
open Microsoft.FSharp.Quotations.Patterns 

let cout (s:string)= System.Console.WriteLine (s) 

let rec getName exprs = 
    let fixDeclaringType (dt:string) = 
     match dt with 
     | fsi when fsi.StartsWith("FSI_") -> "Fsi" 
     | _ -> dt 
    let toStr (xDeclType: System.Type) x = sprintf "%s.%s" (fixDeclaringType xDeclType.Name) x 
    match exprs with 
    | Patterns.Call(_, mi, _) -> 
     toStr mi.DeclaringType mi.Name 
    | Patterns.Lambda(_, expr) -> 
     getName expr 
    | Patterns.PropertyGet (e, pi, li) -> 
     toStr pi.DeclaringType pi.Name 
    | Patterns.FieldGet (_, fi) -> 
     toStr fi.DeclaringType fi.Name 
    | Patterns.NewUnionCase(uci, _) -> 
     toStr uci.DeclaringType uci.Name 
    | expresion -> "unknown_name" 


let value = "" 
let funcky a = a 
let lambdy = fun(x) -> x*2 
type WithStatic = 
    | A | B 
    with static member StaticMethod a = a 
let someIP = System.Net.IPAddress.Parse("10.132.0.48") 


getName <@ value @> |> cout 
getName <@ funcky @> |> cout 
getName <@ lambdy @> |> cout 
getName <@ WithStatic.A @> |> cout 
getName <@ WithStatic.StaticMethod @> |> cout 
getName <@ someIP.MapToIPv4 @> |> cout 
getName <@ System.Net.IPAddress.Parse @> |> cout 
getName <@ System.Net.IPAddress.Broadcast @> |> cout 
Các vấn đề liên quan