如何从纯JavaScript函数恢复源代码?

如何从纯JavaScript函数恢复源代码?,javascript,functional-programming,lambda-calculus,Javascript,Functional Programming,Lambda Calculus,所谓纯,我的意思是在λ演算的意义上,也就是说,一个单参数函数,除了单参数函数和单参数函数调用之外,它的主体上什么都不包含。通过恢复源代码,我指的是变量重命名。那么比如说, 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(

所谓纯,我的意思是在λ演算的意义上,也就是说,一个单参数函数,除了单参数函数和单参数函数调用之外,它的主体上什么都不包含。通过恢复源代码,我指的是变量重命名。那么比如说,

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)
)中它不起作用。有没有更好的原则性方法来解决这个问题


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

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

我想知道这有多低效。

闭包不是这样工作的。创建了对封闭范围的引用,代码保持不变。您可能更希望在JS中实现自己的lambda演算解释器(没有那么复杂),而不是混淆JS函数和JS代码。代码的要点是强制V8引擎将JavaScript函数计算为正常形式,这意味着每个闭包都会崩溃。我已经在JavaScript上编写了几个lambda演算解释器,实际上,其中一个解释器的运行速度渐进地快于V8本身(),但在许多情况下,您无法让解释器的运行速度快于本机函数,所以我特别需要我所要求的。您的问题是
n2(n2)
确实创建了一个JS闭包函数,而不是折叠的函数,因此不提供对其内部的任何访问(在变量上关闭,我的意思是,代码是不够的)。当然,您可以编写自己的
apply(n2[n2])
函数来创建标准形式的函数。此代码实际上应该是对原始问题的编辑。FWIW,如果你解释你为什么要做这件事,你可能会得到更多的回应。(顺便说一句,我是通过你的SO元问题来这里的)。@PM2Ring-如果这回答了原来的问题,那么为什么它应该是一个编辑而不是一个答案?@BSMP:答案是可以的,但我认为它属于这个问题,因为它更像是关于Viclib解决问题的进展报告,它还提到,它并没有完全解决最初的问题,因为Viclib仍然存在与这种方法的效率相关的问题。但我肯定不会投反对票,也不会将其标记为删除!我发布了,如果这个问题的范围不包括效率方面,那么这就是实际的答案。不过,我把这个问题搞砸了,我想这样做是为了迫使对纯JavaScript函数进行求值,以避免这种恶作剧无限期地增长。