【问题标题】:How can you recover the source code from a pure JavaScript function?如何从纯 JavaScript 函数中恢复源代码?
【发布时间】:2015-10-12 22:38:32
【问题描述】:

我所说的 Pure 是指 λ 演算的意义,即,除了单参数函数和单参数函数调用之外,它的主体上不包含任何内容的单参数函数。通过恢复源代码,我的意思是变量重命名。所以,例如,

n2 = function(v0){return function(v1){return v0(v0(v1))}}
console.log(source(n2));
console.log(source(n2(n2)));

应该打印:

function(v0){return function(v0){return v0(v0(v1))}}
function(v0){return function(v0){return v0(v0(v0(v0(v1))))}}

也就是说,第一行显示了函数n2 的原始来源,第二行显示了n2(n2) 的求值返回的函数的来源。

我已经设法实现它如下:

function source(f){
    var nextVarId = 0;
    return (function recur(f){
        if (typeof f === "function"){
            if (f.isVarFunc) return f(null);
            else {
                var varName = "v"+(nextVarId++);
                var varFunc = function rec(res){
                    var varFunc = function(arg){
                        return arg === null
                            ? "("+res.join(")(")+")"
                            : rec(res.concat(recur(arg)));
                    };
                    varFunc.isVarFunc = true;
                    return varFunc;
                };
                varFunc.isVarFunc = true;
                var body = f(varFunc([varName]));
                body     = body.isVarFunc ? body(null) : recur(body);
                return "(function("+varName+"){return "+body+"})";
            };
        } else return f;
    })(f);
};

问题是我使用了一些相当丑陋的方法来标记函数,方法是将它们的名称设置为特定值,并且它不适用于多次应用的函数(例如a(b)(b))。有没有更好的原则性方法来解决这个问题?

编辑:我设法设计了一个似乎在所有情况下都是正确的版本,但它仍然是一个丑陋的无法阅读的无原则的混乱。

【问题讨论】:

  • 闭包不是这样工作的。创建了对封闭范围的引用,代码保持不变。
  • 你可能更想在 JS 中实现自己的 lambda 演算解释器(它并不复杂),而不是弄乱 JS 函数和 JS 代码。
  • 代码的重点是强制 V8 引擎将 JavaScript 函数评估为正常形式,这意味着每个闭包都会崩溃。
  • 我已经在 J​​avaScript 上编写了几个 lambda 演算解释器,实际上,包括一个比 V8 本身(Optlam)渐近更快的解释器,但在许多情况下,你根本无法让解释器运行得比原生函数快,所以我特别需要我问的。
  • 您的问题是 n2(n2) 确实创建了一个 JS 闭包函数,而不是折叠函数,因此不提供对其内部的任何访问(封闭变量,我的意思是代码是不足够的)。您当然可以编写自己的 apply(n2, [n2]) 函数来创建正常形式的函数。

标签: javascript functional-programming lambda-calculus


【解决方案1】:

最后,这是上述混乱的一个相当干净的版本。

// source :: PureFunction -> String
// Evaluates a pure JavaScript function to normal form and returns the 
// source code of the resulting function as a string.
function source(fn){
    var nextVarId = 0;
    return (function normalize(fn){
        // This is responsible for collecting the argument list of a bound
        // variable. For example, in `function(x){return x(a)(b)(c)}`, it
        // collects `a`, `b`, `c` as the arguments of `x`.  For that, it
        // creates a variadic argumented function that is applied to many
        // arguments, collecting them in a closure, until it is applied to
        // `null`. When it is, it returns the JS source string for the
        // application of the collected argument list.
        function application(argList){
            var app = function(arg){
                return arg === null
                    ? "("+argList.join(")(")+")"
                    : application(argList.concat(normalize(arg)));
            };
            app.isApplication = true;
            return app;
        };
        // If we try to normalize an application, we apply
        // it to `null` to stop the argument-collecting.
        if (fn.isApplication) 
            return fn(null);
        // Otherwise, it is a JavaScript function. We need to create an
        // application for its variable, and call the function on it.
        // We then normalize the resulting body and return the JS
        // source for the function.
        else {
            var varName = "v"+(nextVarId++);
            var body    = normalize(fn(application([varName])));
            return "(function("+varName+"){return "+body+"})";
        };
    })(fn);
};

它仍然不完美,但看起来要好得多。它按预期工作:

console.log(source(function(a){return function(b){return a(b)}}))

输出:

(function(v0){return (function(v1){return (v0)((v1))})})

不过,我想知道这是多么低效。

【讨论】:

  • 这段代码确实应该是对原始问题的编辑。 FWIW,如果你解释为什么你想做这件事,你可能会得到更多的回应。 (顺便说一句,我是通过您的 SO Meta 问题来到这里的)。
  • @PM2Ring - 如果这回答了原始问题,那么为什么要编辑而不是答案?
  • @BSMP:ok 作为答案,但恕我直言,它属于问题,因为它更像是 Viclib 解决问题努力的进度报告,并提到它并没有完全解决最初的问题,因为 Viclib 仍然存在与这种方法的效率相关的问题。但我当然不会否决它,或将其标记为删除!
  • 我发布以来,如果这个问题的范围不包括效率方面,那么这就是实际的答案。不过,我把这个问题搞砸了。
  • 我希望这可以强制评估纯 JavaScript 函数,以避免 thunk 无限增长。
猜你喜欢
  • 1970-01-01
  • 2022-12-10
  • 1970-01-01
  • 2011-11-07
  • 2011-01-27
  • 2021-10-30
  • 2016-09-10
  • 2012-04-14
  • 1970-01-01
相关资源
最近更新 更多