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

所以我想用JavaScript实现LISP的一个子集。我被困在与lambda相关的两件事上。如何在JavaScript中实现lambda/anonymous函数

如何实现创建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);

}

现在这个工作,如果我写在参数上面拉姆达它会评估到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;

}

而λ-功能正在创建这样的:

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

}

独立的函数来创建拉姆达:

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;

}

};

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

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

回答:

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

只需删除其中expr[0][0] === 'lambda' || expr[0][0] === 'string'是真实的一部分,而不是只返回形式的评估需要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;

}

以上是 如何在JavaScript中实现lambda/anonymous函数 的全部内容, 来源链接: utcz.com/qa/265220.html

回到顶部