Javascript函数消失

时间:2017-12-12 17:26:28

标签: javascript function lambda lisp

我正在尝试使用JavaScript作为源语言来实现编程语言LISP的一小部分。当这个小的口齿不清是一个字符串,如下所示:

(define func (lambda (args)(body)))

然后将其传递给名为CreateLambda的函数。 例如,传递以下字符串:

(define add (lambda(x y)(+ x y)))

然后由此函数创建Javascript函数;

function createLambda(expr, env) { // lambda args body
    expr.shift(); //remove the "lambda".
    var lambdaArgs = expr.shift(); //get the arguments
    var lambdaBody = expr.shift(); // get the body
    var parentEnvironment = env;
    return function() {
        var lambdaEnvironment = new environment(parentEnvironment, "lambda environment");
        for(var i = 0; i < arguments.length; i++) {
            var tempVal = evaluate(arguments[i], parentEnvironment);
            lambdaEnvironment.add(lambdaArgs[i], tempVal);
        }
        return evaluate(lambdaBody, lambdaEnvironment);
    }
} 

稍后,如果在小型lisp中调用和评估此函数,则类似:

(add 2 3)

在以下情况下,evaluate循环将拾取此字符串:

else if(typeof expr[0] === 'string') {
    var lispFunc = env.get(expr[0]); //find the function in the environment.
    expr.shift(); 
    var lispFuncArgs = [];
    var tempArg = expr.shift();
    while(tempArg != null) { //apply the variables
        lispFuncArgs.push(evaluate(tempArg, env));
        tempArg = expr.shift();
    }
    return lispFunc.apply(this, lispFuncArgs);

第一次传递给evaluate-function时,它按预期工作,返回数字5.

但是,如果再次调用相同的函数。它仅作为&#34; undefined&#34;进入评估循环。

完整的HTML;

<html>
    <head>
        <meta content="text/html; charset=UTF-8" http-equiv="Content-Script-
Type" content="text/javascript">
        <title>LISP in JavaScript</title>
        <script type="text/javascript" src="lisp.js"></script>
    </head>
    <body>
        <form id="repl" name="repl" action="parse(prompt.value)">
               lisp==&gt;
            <input id="prompt" size="200" value="" name="prompt" 
maxlength="512">
            <br>
            <input type=button style="width:60px;height:30px" name="btnEval" 
value="eval" onclick="output(prompt.value)">
            <br>
            </form>
        <div id="debugdiv" style="background-
color:orange;width=100px;height=20px">
        </div>

    </body>
</html>

完整的JavaScript;

//functions for parsing input String

function parse(exp) {
    return readFromTokes(tokenize(exp)); //code
}

function isNumeric(arg) {
    return !isNaN(arg);
}

function readFromTokes(exp) {
   //Create abstract syntax tree
   if(exp.length == 0) {
   }
   var token = exp.shift();
   if(token == '(') {
       var L = [];
       while(exp[0] != ')') {
           L.push(readFromTokes(exp));
       }         
       exp.shift(); //remove end paranthesis
       return L;
   } else {
       if(token == ')') {
           console.log("Unexpected )");
       } else {
           return atom(token);
       }
   }
}

function tokenize(exp) {
    //Convert a program in form of a string into an array (list)
    var re = /\(/g;
    var re2 = /\)/g;
    exp = exp.replace(re, " ( ");
    exp = exp.replace(re2, " ) ");
    exp = exp.replace(/\s+/g, ' ');
    exp = exp.trim().split(" ");
    return exp;
}

function atom(exp){
    if(isNumeric(exp)) {
        return parseInt(exp); //A number is a number
    } else {
        return exp; //Everything else is a symbol
    }
}

function environment(parentEnvironment, name) {
    var bindings = [];
    var parent = parentEnvironment;
    var name = name;

    function add(variable, value) {
        console.log("variable: " + variable + " value: " + value);
        bindings.push([variable, value]);
    }

    function printName() {
        console.log(name);
    }

    function print() {
        console.log("printing environment: ")
        for(var i = 0; i < bindings.length; i++) {
            console.log(bindings[i][0] + " " + bindings[i][1]);
        }
    }

    function get(variable) {
        for(var i = 0; i < bindings.length; i++) {
            if(variable == bindings[i][0]) {
                return bindings[i][1];
            }
        }
        if(parent != null) {
            return parent.get(variable);
        } else {
            console.log("No such variable");
        }
    }

    function getParent(){
        return parent;
    }

    this.add = add;
    this.get = get;
    this.getParent = getParent;
    this.print = print;
    this.printName = printName;
    return this;
}

function addPrimitives(env) {
    env.add("+", function() {
        var s = 0; 
        for(var i = 0; i < arguments.length; i++) { 
            s += arguments[i]; 
        } 
        return s
    });
    env.add("-", function() {
        var s = arguments[0]; 
        for(var i = 1; i < arguments.length; i++) { 
            s -= arguments[i];
        } 
        return s
    });
    env.add("*", function() {
        var s = 1; 
        for(var i = 0; i < arguments.length; i++){ 
            s *= arguments[i];
        } 
        return s
    });
    env.add("/", function(x, y) { return x / y });
}


function createLambda(expr, env) { // lambda args body
    expr.shift(); //remove the "lambda".
    var lambdaArgs = expr.shift(); //get the arguments
    var lambdaBody = expr.shift(); // get the body
    var parentEnvironment = env;
    return function() {
        var lambdaEnvironment = new environment(parentEnvironment, "lambda 
       environment");
        for(var i = 0; i < arguments.length; i++) {
            var tempVal = evaluate(arguments[i], parentEnvironment);
            lambdaEnvironment.add(lambdaArgs[i], tempVal);
        }
        return evaluate(lambdaBody, lambdaEnvironment);
    }
}

function evaluate(expr, env) {
    console.log(expr + " has entered evaluate loop");
    env.printName();
    env.print();
    if(typeof expr === 'string') {
        console.log(expr + " is a symbol");
        return env.get(expr);
    } else if(typeof expr === 'number') {
        console.log(expr + " is a number");
        return expr;
    } else if(expr[0] === 'define') { // (define var value)
        console.log(expr + " is a define statement");
        expr.shift(); 
        var newVar = expr.shift();
        var newVal = evaluate(expr.shift(), env);
        env.add(newVar, newVal);
        return env;
    } else if (expr[0] === 'lambda') { // (lambda args body)
        console.log(expr + " is a lambda statement");
        return createLambda(expr, env);
    } else if (typeof expr[0] === 'string'){
        var lispFunc = env.get(expr[0]);
        expr.shift();
        var lispFuncArgs = [];
        var tempArg = expr.shift();
        while(tempArg != null) {
            lispFuncArgs.push(evaluate(tempArg, env));
            tempArg = expr.shift();
        }
        console.log("Function: " + lispFunc);
        console.log("Arguments: " + lispFuncArgs);
        return lispFunc.apply(this, lispFuncArgs);
    } else {
        console.log(expr + " cannot be interpreted");
    }
}


var globalEnvironment = new environment(null, "Global");

addPrimitives(globalEnvironment);

function start(string) {
    return evaluate(parse(string), globalEnvironment);
}

var output = function (string) {
    try {
        document.getElementById('debugdiv').innerHTML = start(string);
    } catch(e) {
        document.getElementById('debugdiv').innerHTML = e.name + ': ' + e.message;
    }
};

1 个答案:

答案 0 :(得分:2)

这是因为您的代码中存在一个小错误。 当你在第134行调用evaluate(lambdaBody, lambdaEnvironment)时,你提供lambdaBody作为第一个参数,这是&#39;函数中的expr参数evaluate(expr,env)&# 39;功能(行:138)。在评估期间,您使用tempArg = expr.shift();函数使用它,并在其为空时退出循环。第二次,您的lambdaBody很遗憾是一个空数组。

要修复它,您应该更改evaluate函数而不是修改其参数(例如,创建副本并使用副本)

我建议您修复评估功能中的错误,但真正有用的示例是将第134行更改为深层复制 lambdaBody,如下所示:

  evaluate(lambdaBody.slice(0), lambdaEnvironment);

无论你多少次打电话,你都会看到它现在有效。但是bug实际上是在evaluate函数中,所以在那里修复它。