繁体   English   中英

如何在JavaScript中实现lambda / anonymous函数

[英]How to implement lambda /anonymous functions in JavaScript

因此,我正在尝试使用JavaScript实现LISP的子集。 我被困在与lambda有关的两件事上。

如何实现创建lambda的功能,同时为其提供参数并立即对其进行求值? 例如:

((lambda(x)(* x 2)) 3)

现在,我将这种功能硬编码在我的eval循环中,如下所示:

else if (isArray(expr)){
      if (expr[0][0] === 'lambda' || expr[0][0] === 'string') { 
         console.log("This is a special lambda");
         var lambdaFunc = evaluate(expr[0], env)
         var lambdaArgs = [];
         for(var i = 1; i < expr.length; i++){
            lambdaArgs.push(expr[i]);
         }         
         return lambdaFunc.apply(this, lambdaArgs);
      }

现在这行得通了,如果我用参数写上面的lambda,它将计算为6,但是,我想知道是否有更聪明的方法来实现这一点?

如果将lambda绑定到符号,例如:

(define fib (lambda(n)
    (if (< n 2) 1 
    (+ (fib (- n 1))(fib (- n 2)))
    )))

在这种情况下,(定义fib)部分将首先由eval循环求值,就像fib只是被分配了一个数字一样:

else if (expr[0] === 'define') { // (define var value)
        console.log(expr + " is a define statement");
        var newVar = expr[1];
        var newVal = evaluate(expr[2], env);
        env.add(newVar, newVal);
        return env;
    }

lambda函数的创建方式如下:

else if (expr[0] === 'lambda') { // (lambda args body)
        console.log(expr + " is a lambda statement");
        var args = expr[1];
        var body = expr[2];
        return createLambda(args, body, env);
    }

创建lambda的单独函数:

function createLambda(args, body, env){ // lambda args body
   function runLambda(){
         var lambdaEnvironment = new environment(env, "lambda environment");
         for (var i = 0; i < arguments.length; i++){
            lambdaEnvironment.add(args[i], evaluate(arguments[i], env));
         }
         return evaluate(body, lambdaEnvironment);
   }  
   return runLambda;
}

这对于以下lambda效果很好:

(define range (lambda (a b) 
    (if (= a b) (quote ()) 
    (cons a (range (+ a 1) b)))))

(define fact (lambda (n) 
    (if (<= n 1) 1 
    (* n (fact (- n 1))))))

例如,(范围0 10)返回一个从0到10的列表。

但是,如果我在lambda中尝试一个lambda,则它不起作用。 例如:

(define twice (lambda (x) (* 2 x)))  
(define repeat (lambda (f) (lambda (x) (f (f x)))))

我希望以下内容返回40:

((repeat twice) 10)

但是,它返回的列表如下所示:

function runLambda(){ var lambdaEnvironment = new environment(env, "lambda 
environment"); for (var i = 0; i < arguments.length; i++){ 
lambdaEnvironment.add(args[i], evaluate(arguments[i], env)); } return 
evaluate(body, lambdaEnvironment); },10

有什么想法可能会在这里丢失吗?

完整的JavaScript;

//functions for parsing invoice String

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


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

function isArray(obj){
    return !!obj && obj.constructor === Array;
}

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");
            return false;
        }
    }

    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 });
   env.add("false", false);
   env.add("true", true);
   env.add(">", function(x, y){ return (x > y) });
   env.add("<", function(x, y){ return (x < y) }); 
   env.add("=", function(x, y){ return (x === y)});
   env.add(">=", function(x, y){ if (x >= y){return true;} else {return false;}}); 
   env.add("<=", function(x, y){ if (x <= y){return true;} else {return false;}});
   env.add("eq?", function() {var s = arguments[0]; var t = true; for(var i = 1; i<arguments.length; i++){ if (arguments[i] != s) {t = false }} return t;});
   env.add("cons", function(x, y) { var temp = [x]; return temp.concat(y); });
   env.add("car", function(x) { return x[0]; });
   env.add("cdr", function(x) { return x.slice(1); });
   env.add("list", function () { return Array.prototype.slice.call(arguments); });
   env.add("list?", function(x) {return isArray(x); });
   env.add("null", null);
   env.add("null?", function (x) { return (!x || x.length === 0); });
}


function createLambda(args, body, env){ // lambda args body
   function runLambda(){
     var lambdaEnvironment = new environment(env, "lambda environment");
         for (var i = 0; i < arguments.length; i++){
            lambdaEnvironment.add(args[i], evaluate(arguments[i], env));
         }
         return evaluate(body, lambdaEnvironment);
   }  
   return runLambda;
}

function evaluate(expr, env) {
    console.log(expr + " has entered evaluate loop");
    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");
        var newVar = expr[1];
        var newVal = evaluate(expr[2], env);
        env.add(newVar, newVal);
        return env;
    } else if (expr[0] === 'lambda') { // (lambda args body)
        console.log(expr + " is a lambda statement");
        var args = expr[1];
        var body = expr[2];
        return createLambda(args, body, env);
    } else if (expr[0] === 'quote') {
        return expr[1];
    } else if (expr[0] === 'cond'){
        console.log(expr + " is a conditional");
        for (var i = 1; i < expr.length; i++){
            var temp = expr[i];
            if (evaluate(temp[0], env)) {
                console.log(temp[0] + " is evaluated as true");
                return evaluate(temp[1], env);
            }
        }
        console.log("no case was evaluated as true");
        return;
    } else if (expr[0] === 'if') {
        console.log(expr + "is an if case");
        return function(test, caseyes, caseno, env){
            if (test) {
                return evaluate(caseyes, env);
            } else {
                return evaluate(caseno, env);
            }
        }(evaluate(expr[1], env), expr[2], expr[3], env); 
    } else if (typeof expr[0] === 'string'){
        console.log(expr + " is a function call");
        var lispFunc = env.get(expr[0]);
        var lispFuncArgs = [];        
        for(var i = 1; i < expr.length; i++){
        lispFuncArgs.push(evaluate(expr[i], env));
       }
       return lispFunc.apply(this, lispFuncArgs);
    } else if (isArray(expr)){
        if (expr[0][0] === 'lambda' || expr[0][0] === 'string') { 
            console.log("This is a special lambda");
            var lambdaFunc = evaluate(expr[0], env)
            var lambdaArgs= [];
            for(var i = 1; i < expr.length; i++){
                lambdaArgs.push(expr[i]);
            }         
            return lambdaFunc.apply(this, lambdaArgs);
        } else { 
             console.log(expr + " is a list");
             var evaluatedList = [];
             for(var i = 0; i < expr.length; i++){
                 evaluatedList.push(evaluate(expr[i], env));
             }
             return evaluatedList;
        }
    } 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;
    }
};

完整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>

当然也欢迎进一步的想法,建议和意见。 谢谢!

你不检查操作数的结构,看它是否是一个lambdaeval的操作。 eval的标准方法是检查它是否为原始类型,然后检查特殊形式和宏,然后在应用之前评估运算符和操作数。

只需删除expr[0][0] === 'lambda' || expr[0][0] === 'string' expr[0][0] === 'lambda' || expr[0][0] === 'string'为true,您不仅需要返回形式的求值,还需要apply操作数:

else if (isArray(expr)){
  const fn = evaluate(expr[0], env);
  const evaluatedList = [];
  for(var i = 1; i < expr.length; i++){
    evaluatedList.push(evaluate(expr[i], env));
  }
  return fn(...evaluatedList);
}

由于您在错误的环境中评估参数,因此createLambda是错误的。 这是正确的,因为已经对参数进行了评估:

function createLambda(args, body, env){ // lambda args body
   function runLambda(){
     const lambdaEnvironment = new environment(env, "lambda environment");
     for (let i = 0; i < arguments.length; i++){
       lambdaEnvironment.add(args[i], arguments[i]);
     }
     return evaluate(body, lambdaEnvironment);
   }  
   return runLambda;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM