Angel "Java" Lopez

NET, Java, PHP y Desarrollo de Software

This Blog

Syndication

Search

Tags

Community

Email Notifications

Archives

.NET

ASP.NET

Windows Form

VB.NET

C#

Sitios

Blogs

AjLisp en Javascript (Parte 3) Define, Lambda y Closures

Anterior Post

Veamos la definición de nuevas formas en AjLisp, mi intérprete Lisp escrito en Javascript (github repository). Un forma especial clave en AjLisp es la define:

var defineForm = new SpecialForm();
defineForm.eval = function eval(list, env)
{
	var name = list.first().name();
	var value = list.rest().first();
	var body = list.rest().rest();
		
	if (isNil(body)) {
		value = evaluate(value, env);
	}
	else {
		value = new Closure(value, env, body);
	}		
		
	environment[name] = value;
	return value;
}

Se puede usar para definir datos simples globales. Mis primeros tests:

test("Evaluate Simple Expression with Atoms", function() {
	eval("(define one 1)");
	eval("(define two 2)");
	eval("(define three 3)");
	equal(eval("one"), 1);
	equal(eval("(quote one)").asString(), "one");
	equal(eval("(list one two three)").asString(), "(1 2 3)");
	equal(eval("(first (list one two three))"), 1);
	equal(eval("(rest (list one two three))").asString(), "(2 3)");
	equal(eval("(cons one (list two three))").asString(), "(1 2 3)");
});
		

Pero define está preparada también para recibir tres parámetros:

(define mapfirst (fn lst)
  (if (nilp lst)
    nil
    (cons
      (fn (first lst))
      (mapfirst fn (rest lst))
    )
  )
)

El primer parámetro es el nombre de la propiedad a definir en el ambiente (environment) global. El segundo parámetro es una lista de parámetros. El tercero es la forma a aplicar. Entonces, define se puede usar para definir nuevas funciones, formas. Es equivalente a

(define mapfirst (lambda (fn lst)
    (if (nilp lst)
      nil
      (cons
        (fn (first lst))
        (mapfirst fn (rest lst))
      )
    )
  )
)

Vean el lambda. Es una forma especial:

var lambdaForm = new SpecialForm();
lambdaForm.eval = function eval(list, env)
{
    var argnames = list.first();
    var body = list.rest();
    return new Closure(argnames, env, body);
}

Tanto define (con tres parámetros) como lambda terminan creando un Closure. La clausura recibe una lista de nombres de argumentos, un ambiente (closure environment) y un cuerpo. Entonces, cuando un closure se evalúa:

function Closure(argnames, closureenv, body) {
	body = new List(doForm, body);
	
	this.eval = function(args, environment) {
		var newenv = makeEnvironment(argnames, args, closureenv);
		return evaluate(body, newenv);
	};
}
	
Closure.prototype.evaluate = Form.prototype.evaluate;
Closure.prototype.apply = Form.prototype.apply;

La parta clave es el .eval: la evalucación de closure emplea un nuevo environment, basado NO EN EL ACTUAL, sino en el closureenv, el ambiente recibido en el constructor cuando la clausura fue creada. Esta es una típica implementación en un intérprete Lisp.

Próximos temas a discutir en posts: flambda, mlambda, evaluación de macros, y el parser.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Published Tue, Sep 6 2011 12:05 by lopez

Comments

# AjLisp en Ruby (1) Estructura, Clases y Tests@ Saturday, December 03, 2011 4:06 AM

Estoy aprendiendo y practicando Ruby, y como es costumbre, lo hago escribiendo algo interesante para

Angel "Java" Lopez

Leave a Comment

(required) 
(required) 
(optional)
(required) 
If you can't read this number refresh your screen
Enter the numbers above: