2011-06-20 36 views
5

Tôi có chút mã này:F # trích dẫn: biến có thể thoát khỏi phạm vi

let rec h n z = if n = 0 then z 
       else <@ (fun x -> %(h (n - 1) <@ x + %z @>)) n @> 

chuyển đổi từ một ví dụ MetaOcaml trong http://www.cs.rice.edu/~taha/publications/journal/dspg04a.pdf

Trong bài báo có được giải thích rằng ví dụ trên sẽ mang lại những điều sau với các thông số 3.<1>. (trong ký hiệu MetaOcaml):

.<(fun x_1 -> (fun x_2 -> (fun x_3 -> x_3 + (x_2 + (x_1 + 1))) 1) 2) 3>. 

Như bạn có thể thấy x ´s được thay thế bằng x_1, x_2 v.v. vì x nếu không sẽ chỉ đề cập đến số x ở trong cùng fun.

Nhưng trong F #, điều này không được phép. Tôi nhận được lỗi biên dịch thời gian: "Biến 'x' bị ràng buộc trong một báo giá nhưng được sử dụng như là một phần của một biểu thức nối. Điều này không được phép vì nó có thể thoát khỏi phạm vi của nó." Vì vậy, câu hỏi là: làm thế nào điều này có thể được thay đổi để nó sẽ biên dịch và có cùng ngữ nghĩa như đầu ra MetaOcaml?

Cập nhật để nhận xét: Tôi sử dụng PowerPack để thực sự đánh giá báo giá. Nhưng tôi không nghĩ rằng điều này có liên quan gì đến nó bởi vì lỗi ở thời gian biên dịch. Cho đến nay QuotationEvaluation hoạt động. Tuy nhiên, tôi biết nó có thể không được thực hiện hiệu quả nhất.

Cập nhật câu trả lời của Tomas´: Tôi thực sự không muốn x là toàn cầu hoặc để thoát khỏi phạm vi. Nhưng tôi muốn tương đương với

let rec h n z = if n = 0 then z 
       else (fun x -> (h (n - 1) (x + z))) n 

với trích dẫn. Câu trả lời của bạn cung cấp cho (h 3 <@ 1 @>).Eval() = 4 nơi sản lượng trên h 3 1 = 7. Và ở đây, tôi muốn 7 là câu trả lời.

+0

Tôi không biết MeyaOCaml, nhưng có vẻ như bạn đang cố gắng sử dụng biến từ báo giá lambda trong mã F # thực. Báo giá không thực sự là mã F #, và vì lý do đó bạn cũng không thể đánh giá chúng (ngoại trừ với bộ đánh giá LINQ, không hoàn hảo). –

Trả lời

6

Cú pháp báo giá F # không hỗ trợ các biến có khả năng thoát khỏi phạm vi, vì vậy bạn sẽ cần phải xây dựng cây một cách rõ ràng bằng cách sử dụng các hoạt động Expr. Một cái gì đó như thế này nên làm các trick:

open Microsoft.FSharp.Quotations 

let rec h n (z:Expr<int>) = 
    if n = 0 then z     
    else 
    let v = new Var("x", typeof<int>) 
    let ve = Expr.Var(v) 
    Expr.Cast<int> 
     (Expr.Application(Expr.Lambda(v, h (n - 1) <@ %%ve + %z @>), 
          Expr.Value(n))) 

Tuy nhiên, đây là ví dụ khá nhân tạo (để chứng minh biến chụp trong MetaOCaml, không có sẵn trong F #). Nó chỉ tạo ra biểu thức như (2 + (1 + ...)). Bạn có thể nhận được kết quả tương tự bằng cách viết một cái gì đó như thế này:

let rec h n (z:Expr<int>) = 
    if n = 0 then z     
    else h (n - 1) <@ n + %z @> 

Hoặc thậm chí tốt hơn:

[ 1 .. 4 ] |> List.fold (fun st n -> <@ n + %st @>) <@ 0 @> 

Tôi cũng đã accross hạn chế này trong F # trích dẫn và nó sẽ được tốt đẹp nếu điều này được hỗ trợ. Tuy nhiên, tôi không nghĩ rằng đó là một vấn đề lớn trong thực tế, bởi vì F # trích dẫn không được sử dụng cho lập trình meta theo giai đoạn. Chúng hữu ích hơn trong việc phân tích mã F # hiện có hơn để tạo mã.

+0

Bạn có vẻ như một chàng trai có năng lực :) Nhưng điều này không hoàn toàn là những gì tôi muốn. Tôi có thể đã không đủ rõ ràng trong câu hỏi của mình, vì vậy sẽ có bản cập nhật của câu hỏi trong 2 giây. –

+0

@lasseespeholt: Tôi đã thay đổi phiên bản để sử dụng 'Var mới 'thay vì' Var.Global' (chỉ sai). Điều này bây giờ sẽ khai báo một biến mới trong mỗi lần lặp (chúng sẽ có cùng _name_, nhưng sẽ là _different_ biến). –

+0

@Tomas Thanks :) hoạt động như mong đợi. Tuy nhiên, tôi đoán tôi sẽ quay trở lại MetaOcaml sau đó bởi vì tôi muốn sử dụng nó cho lập trình đa giai đoạn. –

Các vấn đề liên quan