Цитаты F #: переменная может выходить за рамки - PullRequest
5 голосов
/ 20 июня 2011

У меня есть этот бит кода:

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

, преобразованный из примера MetaOcaml в http://www.cs.rice.edu/~taha/publications/journal/dspg04a.pdf

В статье объясняется, что приведенный выше пример даст следующее спараметры 3 и .<1>. (в обозначении MetaOcaml):

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

Как вы можете видеть, x ´ заменяется на x_1, x_2 и т. д., поскольку xв противном случае будет ссылаться только на x в самом внутреннем fun.

Но в F # это не разрешено.Я получаю ошибку во время компиляции: «Переменная 'x' связана в кавычке, но используется как часть объединенного выражения. Это недопустимо, поскольку она может выходить за пределы своей области».Итак, вопрос: как это можно изменить, чтобы он компилировался и имел ту же семантику, что и выходные данные MetaOcaml?

Обновление до комментария: Я использую PowerPack для фактической оценки предложения.Но я не думаю, что это имеет какое-либо отношение к этому, потому что ошибка во время компиляции.Пока что QuotationEvaluation работает.Однако я знаю, что это, возможно, не самая эффективная реализация.

Обновление ответа Томаса: Я действительно не хочу, чтобы x был глобальным или выходил из области видимости.,Но я хочу это эквивалент

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

с цитатами.Ваш ответ дает (h 3 <@ 1 @>).Eval() = 4, где выше дает h 3 1 = 7.И здесь я хочу, чтобы ответом стал 7.

1 Ответ

6 голосов
/ 20 июня 2011

Синтаксис цитаты F # не поддерживает переменные, которые потенциально могут выходить за рамки видимости, поэтому вам нужно явно построить дерево, используя операции Expr. Нечто подобное должно сработать:

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)))

Однако это довольно искусственный пример (для демонстрации захвата переменных в MetaOCaml, который недоступен в F #). Он просто генерирует выражение как (2 + (1 + ...)). Вы можете получить тот же результат, написав что-то вроде этого:

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

Или даже лучше:

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

Я также столкнулся с этим ограничением в цитатах F #, и было бы неплохо, если бы это было поддержано. Однако я не думаю, что это такая большая проблема на практике, потому что цитаты F # не используются для поэтапного метапрограммирования. Они более полезны для анализа существующего кода F #, чем для генерации кода.

...