ããã«ã¡ã¯ JavaScriptããã°ã©ãã³ã°èšèªãå®è£
ããããã®ã¬ã€ãã PLãã¥ãŒããªã¢ã«ã®ç¿»èš³ã®ç¬¬2éšã玹ä»ããŸãã
翻蚳è
ãã
ç¬èªã®ããã°ã©ãã³ã°èšèª-λèšèª ïŒå
ã®èšèª -λanguageïŒãäœæããŸãã äœæã®ããã»ã¹ã§ã¯ãååž°éäžãã³ã³ãããŒã«è»¢éã¹ã¿ã€ã«ãåºæ¬çãªæé©åææ³ãªã©ãå€ãã®è峿·±ãææ³ã䜿çšããŸãã ã€ã³ã¿ããªã¿ã®2ã€ã®ããŒãžã§ã³ãäœæãããŸã-éåžžã®ã€ã³ã¿ããªã¿ãšCPSã€ã³ã¿ããªã¿ãJavaScriptã®ãã©ã³ã¹ã³ã³ãã€ã©ã
ãªãªãžãã«ã®äœè
ã¯ãæåãªUglifyJSã©ã€ãã©ãªïŒJSã³ãŒããæå°åããã³ãã©ãŒãããããããã®ããŒã«ïŒã®äœè
ã§ããMihai Bazonã§ãã
PSã€ã³ã¿ããªã¿ãšã³ã³ãã€ã©ã«ãã°ããããŸãïŒ a() && b()
ãŸãã¯a() || b()
ãããªåŒã§ a() || b()
äž¡æ¹ã®éšåãåžžã«å®è¡ãããŸãã ãã¡ãããããã¯ééã£ãŠa()
&&
æŒç®åã«å¯ŸããŠa()
åœã§ãããã ||
ã«å¯ŸããŠåœã§ã¯ãªãããã§ãã ã b()
å€ã¯äœã®åœ¹å²ãæãããŸããã ãããä¿®æ£ããã®ã¯é£ãããããŸããã
ã·ã³ãã«ãªéèš³
åã®ããŒãã§ã¯ã InputStream
ã TokenStream
ããã³parse
3ã€ã®é¢æ°ãTokenStream
ããŸããã ã³ãŒãããASTãååŸããã«ã¯ã次ã®ã³ãŒãã䜿çšããŸãã
var ast = parse(TokenStream(InputStream(code)));
ã€ã³ã¿ããªã¿ã®èšè¿°ã¯ãããŒãµãŒãããç°¡åã§ããããªãŒãååž°çã«èµ°æ»ããéåžžã®é åºã§åŒãå®è¡ããã ãã§ãã
ã³ã³ããã¹ãïŒ Environment
ïŒ
é©åãªã³ãŒããå®è¡ããã«ã¯ãã³ã³ããã¹ããå¿
èŠã§ããããã¯ãç¹å®ã®å Žæã«ãããã¹ãŠã®å€æ°ãå«ããªããžã§ã¯ãã§ãã evaluate
颿°ã®åŒæ°ãšããŠæž¡ãããŸãã
ã©ã ãããŒãã«å
¥ããã³ã«ãã³ã³ããã¹ãã«æ°ãã倿°ã远å ããå¿
èŠããããŸã-颿°ã®åŒæ°ã åŒæ°ãå€éšãããã¯ããã®å€æ°ãšéè€ããå Žåã颿°ãçµäºããåŸã«å€æ°ã®å€ãå€ã埩å
ããå¿
èŠããããŸãã
ãããè¡ãæãç°¡åãªæ¹æ³ã¯ãJavaScriptãããã¿ã€ãç¶æ¿ã䜿çšããããšã§ãã æ°ãã颿°ãå
¥åãããšãæ°ããã³ã³ããã¹ããäœæããå€éšã³ã³ããã¹ãããã®ãããã¿ã€ããšããŠèšå®ããæ°ããã³ã³ããã¹ãã§é¢æ°ãåŒã³åºããŸãã ãã®ãããã§ãç§ãã¡ã«ã¯äœããããŸãã-å€éšã®ã³ã³ããã¹ãã§ã¯ããã®å€æ°ã¯ãã¹ãŠæ®ããŸãã
Environment
ãªããžã§ã¯ãã®å®è£
ã¯æ¬¡ã®ãšããã§ãã
function Environment(parent) { this.vars = Object.create(parent ? parent.vars : null); this.parent = parent; } Environment.prototype = { extend: function() { return new Environment(this); }, lookup: function(name) { var scope = this; while (scope) { if (Object.prototype.hasOwnProperty.call(scope.vars, name)) return scope; scope = scope.parent; } }, get: function(name) { if (name in this.vars) return this.vars[name]; throw new Error("Undefined variable " + name); }, set: function(name, value) { var scope = this.lookup(name);
Environment
ãªããžã§ã¯ãã«ã¯ãå€éšã³ã³ããã¹ããæãparent
ãã£ãŒã«ãããããŸãã ã°ããŒãã«ã³ã³ããã¹ãã®å Žåã¯null
ã«ãªããŸãã vars
ãã£ãŒã«ããããããã®ã³ã³ããã¹ãã«å±ãããã¹ãŠã®å€æ°ããããŸãã ã°ããŒãã«ã³ã³ããã¹ãã®å Žåã空ã®ãªããžã§ã¯ãïŒ Object.create(null)
ïŒããã³éã°ããŒãã«ã³ã³ããã¹ãã®èŠªã³ã³ããã¹ãã®å€æ°ã®ã³ããŒïŒ Object.create(parent.vars)
ïŒã«ããã«çãããªããŸãã
ããã€ãã®æ¹æ³ããããŸãïŒ
extend()
-çŸåšã®ã³ã³ããã¹ããã³ããŒããŸããlookup(name)
ãšããname
ã®å€æ°name
å®çŸ©ãããŠããã³ã³ããã¹ããèŠã€ããŸããget(name)
-nameãšããname
倿°ã®å€ãååŸããŸãã 倿°ãå®çŸ©ãããŠããªãå ŽåãäŸå€ãã¹ããŒããŸããset(name, value)
-倿°ã®å€ãèšå®ããŸãã ãã®ã¡ãœããã¯ã倿°ãå®çŸ©ãããŠããã³ã³ããã¹ããæ¢ããŸãã å®çŸ©ãããŠããããã°ããŒãã«ã³ã³ããã¹ãã«ãªãå ŽåãäŸå€ãã¹ããŒãããŸããdef(name, value)
-çŸåšã®ã³ã³ããã¹ãã§å€æ°ãäœæïŒãŸãã¯ãªãŒããŒã©ãããŸãã¯ãªãŒããŒã©ã€ãïŒããŸãã
evaluate
颿°
Environment
ãªããžã§ã¯ããã§ããã®ã§ã次ã«äž»ãªåé¡ã®è§£æ±ºã«é²ã¿ãŸãã ãã®é¢æ°ã¯ãéä¿¡ããŒãã®ã¿ã€ãã«å¿ããŠããã€ãã®ã¢ã¯ã·ã§ã³ãå®è¡ãã倧ããªswitch
ãããã¯ã«ãªããŸãã
function evaluate(exp, env) { switch (exp.type) {
ãªãã©ã«ã®å Žåãåã«ãã®å€ãè¿ããŸãã
case "num": case "str": case "bool": return exp.value;
倿°ã¯ã³ã³ããã¹ãããååŸãããŸãïŒå€æ°ã®ååã¯value
ãã£ãŒã«ãã«å«ãŸããŠãvalue
ïŒã
case "var": return env.get(exp.value);
å²ãåœãŠãã«ã¯ãå·ŠåŽã«å€æ°ã®ååïŒããŒãvar
ïŒãããããšã確èªããå¿
èŠããããŸãã ããã§ãªãå Žåã¯ãåã«äŸå€ãã¹ããŒããŸãïŒå€æ°ä»¥å€ãžã®ä»£å
¥ã¯ãµããŒãããŠããŸããïŒã 次ã«ã env.set
ã䜿çšããŠå€æ°ã®å€ãèšå®ããŸãã åŒã®å³èŸºã¯ã evaluate
ããååž°åŒã³åºãã䜿çšããŠèšç®ããå¿
èŠãããããšã«æ³šæããŠãã ããã
case "assign": if (exp.left.type != "var") throw new Error("Cannot assign to " + JSON.stringify(exp.left)); return env.set(exp.left.value, evaluate(exp.right, env));
ã¿ã€ããbinary
ããŒãã®å Žåãäž¡æ¹ã®ãªãã©ã³ãã«æŒç®åãé©çšããå¿
èŠããããŸãã apply_op
颿°ã¯åŸã§èšè¿°ããŸãã ãŸããåŒã®äž¡æ¹ã®éšåã«å¯ŸããŠevaluate
ãåŒã³åºããŸãã
case "binary": return apply_op(exp.operator, evaluate(exp.left, env), evaluate(exp.right, env));
lambda
åã®ããŒãã¯éåžžã®JavaScriptã¯ããŒãžã£ãŒãè¿ããããJavaScriptããã§ãéåžžã®é¢æ°ã®ããã«åŒã³åºãããšãã§ããŸãã make_lambda
颿°ã远å ããŸãããããã«ã€ããŠã¯åŸã§èª¬æããŸãã
case "lambda": return make_lambda(env, exp);
if
ããŒãã®å®è¡if
éåžžã«ç°¡åã§ããæåã«æ¡ä»¶ã®å€ãèŠã€ããŸãã falseã§ãªãå Žåã¯ã then
ãã©ã³ãã®å€ãè¿ããŸãã ãã以å€ã®å Žåã else
ãã©ã³ããããå Žåããã®å€ããŸãã¯false
ïŒ
case "if": var cond = evaluate(exp.cond, env); if (cond !== false) return evaluate(exp.then, env); return exp.else ? evaluate(exp.else, env) : false;
prog
ããŒãã¯äžé£ã®åŒã§ãã ãã¹ãŠã®åŒãé çªã«å®è¡ããåŸè
ã®å€ãååŸããŸãïŒç©ºã®ã·ãŒã±ã³ã¹ã®å€ã¯false
ïŒã
case "prog": var val = false; exp.prog.forEach(function(exp){ val = evaluate(exp, env) }); return val;
call
ã¿ã€ãã®ããŒãã®å Žåã颿°ãåŒã³åºãå¿
èŠããããŸãã ãã®åã«ã颿°èªäœã®å€ãèŠã€ãããã¹ãŠã®åŒæ°ã®å€ãèŠã€ãã apply
ã䜿çšããŠé¢æ°ãåŒã³åºãapply
ã
case "call": var func = evaluate(exp.func, env); return func.apply(null, exp.args.map(function(arg){ return evaluate(arg, env); }));
ããã«ã¯å°éããŸããããããŒãµãŒã«æ°ããããŒãã¿ã€ãã远å ããã€ã³ã¿ãŒããªã¿ãŒã«è¿œå ããã®ãå¿ããå Žåã¯ã次ã®ããã«ããŸãã
default: throw new Error("I don't know how to evaluate " + exp.type); } }
ãããéèš³ã®äž»èŠéšåã§ããã äžèšã§ã¯ããŸã å®è£
ããŠããªã2ã€ã®é¢æ°ã䜿çšããã®ã§ãå§ããŸãããã
apply_op
ïŒ
function apply_op(op, a, b) { function num(x) { if (typeof x != "number") throw new Error("Expected number but got " + x); return x; } function div(x) { if (num(x) == 0) throw new Error("Divide by zero"); return x; } switch (op) { case "+" : return num(a) + num(b); case "-" : return num(a) - num(b); case "*" : return num(a) * num(b); case "/" : return num(a) / div(b); case "%" : return num(a) % div(b); case "&&" : return a !== false && b; case "||" : return a !== false ? a : b; case "<" : return num(a) < num(b); case ">" : return num(a) > num(b); case "<=" : return num(a) <= num(b); case ">=" : return num(a) >= num(b); case "==" : return a === b; case "!=" : return a !== b; } throw new Error("Can't apply operator " + op); }
æŒç®åã®ã¿ã€ããšåŒæ°ãåãåããŸãã ã·ã³ãã«ã§çŽæçãªswitch
ã 倿°ã®ãããªä»»æã®å€ããšãããšãã§ããJavaScriptãšã¯ç°ãªããæå³ããªããªããã®ãã§ãã ç®è¡æŒç®åã®ãªãã©ã³ãã¯æ°å€ã§ããããŒãã«ããé€ç®ãèš±å¯ããªãããšãå¿
èŠã§ãã æååã«ã€ããŠã¯ãåŸã§äœããèããŸãã
make_lambda
ïŒ
function make_lambda(env, exp) { function lambda() { var names = exp.vars; var scope = env.extend(); for (var i = 0; i < names.length; ++i) scope.def(names[i], i < arguments.length ? arguments[i] : false); return evaluate(exp.body, scope); } return lambda; }
äžèšã®ããã«ãæž¡ãããã³ã³ããã¹ããšAST颿°ã䜿çšããéåžžã®JavaScript颿°ãè¿ããŸãã ãã¹ãŠã®äœæ¥ã¯ã颿°èªäœãåŒã³åºããããšãã«ã®ã¿å®è¡ãããŸããã³ã³ããã¹ããäœæãããåŒæ°ãèšå®ãããŸãïŒååã§ãªãå Žåã¯false
ã«ãªãfalse
ïŒã 次ã«ã颿°ã®æ¬äœãæ°ããã³ã³ããã¹ãã§å®è¡ãããŸãã
ãã€ãã£ã颿°
ã芧ã®ãšãããç§ãã¡ã®èšèªã®JavaScriptãšããåãããæ¹æ³ã¯ãããŸããã§ããã 以åã¯print
ããã³println
颿°ã䜿çšããŠããŸããããã©ãã«ãå®çŸ©ããŠããŸããã§ããã JavaScriptã§èšè¿°ããã°ããŒãã«ã³ã³ããã¹ãã«è¿œå ããã ãã§ãã
ãã®ãããªã³ãŒãã®äŸã次ã«ç€ºããŸãã
å
šã³ãŒã
ãããŸã§ã«äœæãããã¹ãŠã®ã³ãŒããããŠã³ããŒãã§ããŸã ã NodeJSã䜿çšããŠèµ·åã§ããŸãã ã³ãŒããæšæºã¹ããªãŒã ã«æž¡ãã ãã§ãïŒ
echo 'sum = lambda(x, y) x + y; println(sum(2, 3));' | node lambda-eval1.js
ã³ãŒãäŸ
ç§ãã¡ã®ããã°ã©ãã³ã°èšèªã¯ãã·ã³ãã«ã§ãããäžè¬çã«ã³ã³ãã¥ãŒã¿ãŒã§è§£æ±ºã§ããåé¡ãïŒçè«çã«ã¯ïŒè§£æ±ºã§ããŸãã ããã¯ãç§ãããè³¢ãç·-ã¢ãã³ãŸãã£ãŒããšã¢ã©ã³ãã¥ãŒãªã³ã°-ãäžåºŠãλèšç®ïŒã©ã ãèšç®ïŒããã¥ãŒãªã³ã°ãã·ã³ã«çžåœããλèšèªãλèšç®ãå®è£
ããããšã蚌æããããã§ãã
ããã¯ãããšãç§ãã¡ã®èšèªã«æ©äŒããªããŠãããã§ã«æã£ãŠãããã®ã䜿ã£ãŠåãããšãå®çŸã§ããããšãæå³ããŸãã ãŸãã¯ããããå°é£ãªå Žåã¯ãå¥ã®èšèªã®ã€ã³ã¿ãŒããªã¿ãŒãäœæã§ããŸãã
ãµã€ã¯ã«
ååž°ãããã°ã«ãŒãã¯åé¡ã«ãªããŸããã ååž°ã®äžã«å®è£
ãããã«ãŒãã®äŸããã§ã«ç€ºããŸããã ããäžåºŠè©ŠããŠã¿ãŸãããã
print_range = λ(a, b) if a <= b { print(a); if a + 1 <= b { print(", "); print_range(a + 1, b); } else println(""); }; print_range(1, 10);
ããããããã§åé¡ããããŸããããšãã°ãååŸ©åæ°ã1000ã«å¢ãããšã600ã®åŸã«ãMaximum call stack size exceededããšãããšã©ãŒã衚瀺ãããŸããããã¯ãã€ã³ã¿ãŒããªã¿ãŒãååž°çã§ååž°ãæå€§æ·±åºŠã«éããããã«çºçããŸãã
ããã¯æ·±å»ãªåé¡ã§ããã解決çããããŸãã ç¹°ãè¿ãã®ããã«ïŒ for
ãŸãã¯while
ïŒæ°ããã³ã³ã¹ãã©ã¯ãã远å ãããã®ã§ããããããã䜿ããã«ãã£ãŠã¿ãŸãããã ååž°ã¯ãããã«èŠããã®ã§ããããæ®ããŸãããã ãã®å¶éãåé¿ããæ¹æ³ã«ã€ããŠã¯åŸã§èª¬æããŸãã
ããŒã¿æ§é ïŒãã®æ¬ åŠïŒ
λèšèªã«ã¯ãæ°å€ãæååãããŒã«åã®3çš®é¡ã®ããŒã¿ããããŸãã é
åããªããžã§ã¯ããªã©ã®è€éãªã¿ã€ããäœæããããšã¯äžå¯èœã«æãããããããŸããã ããããããã¯tatã§ã¯ãããŸããããã1ã€ã®ã¿ã€ãããããŸãïŒfunctionã λèšç®ã«åŸããšãç¶æ¿ãå«ããŠããªããžã§ã¯ããå«ãä»»æã®ããŒã¿æ§é ãäœæã§ããããšãããããŸãã
ãªã¹ãã®äŸã§ç€ºããŸãã 2ã€ã®å€ãå«ããªããžã§ã¯ããäœæããcons
颿°ããããšããŸãã ãã®ãªããžã§ã¯ãããã»ã«ããŸãã¯ããã¢ããšåŒã³ãŸãããã å€ã®1ã€ã«ãcarãããã1ã€ã«ãcdrããšããååãä»ããŸãã Lispã§ã¯ããåŒã°ããŠããããã§ãã ãªããžã§ã¯ããã»ã«ããããå Žåã颿°car
ããã³cdr
ã䜿çšããŠãã®å€ãååŸã§ããŸãã
x = cons(10, 20); print(car(x));
ããã§ããªã¹ããç°¡åã«å®çŸ©ã§ããŸãã
ãªã¹ãã¯ããcarãã®æåã®èŠçŽ ãšãcdrãã®æ®ãã®èŠçŽ ãå«ããã¢ã§ãã ãããã `cdr`ã«ã¯1ã€ã®å€ããå«ããããšãã§ããŸããïŒ ãã®å€ã¯ãªã¹ãã«ãªããŸãã ãªã¹ãã¯ããcarãã®æåã®èŠçŽ ãšãcdrãã®æ®ãã®èŠçŽ ãå«ããã¢ã§ãã ãããã `cdr`ã«ã¯1ã€ã®å€ããå«ããããšãã§ããŸããïŒ ãã®å€ã¯ãªã¹ãã«ãªããŸãã [...]
ããã¯ååž°çãªããŒã¿åã§ãã ãããã1ã€ã®åé¡ãæ®ã£ãŠããŸãããã€åæ¢ããå¿
èŠãããã®ã§ããããã è«ççã«ã¯ã cdr
ã空ã®ãªã¹ãã«ãªã£ãã忢ããå¿
èŠããããŸãã ãããã空ã®ãªã¹ããšã¯äœã§ããïŒ ãããè¡ãã«ã¯ããNILããšããæ°ãããªããžã§ã¯ãã远å ããŸãããã ãã¢ãšããŠäœ¿çšã§ããŸãïŒ cdr
ãšcdr
ã䜿çšã§ããŸãããçµæã¯NIL
ãã®ãã®ã«ãªããŸãïŒã ããã§ã¯ãã¢ã€ãã 1ã2ã3ã4ã5ã®ãªã¹ããäœæããŸãããã
x = cons(1, cons(2, cons(3, cons(4, cons(5, NIL))))); print(car(x));
ãã®ããã®ç¹å¥ãªæ§æããªãå Žåãã²ã©ãèŠããŸãã ããããããã¯æ¢åã®Î»èšèªæ©èœã䜿çšããŠå®è¡ã§ããããšã瀺ãããã£ãã ãã§ãã å®è£
ã¯æ¬¡ã®ãšããã§ãã
cons = λ(a, b) λ(f) f(a, b); car = λ(cell) cell(λ(a, b) a); cdr = λ(cell) cell(λ(a, b) b); NIL = λ(f) f(NIL, NIL);
ãã®æ¹æ³ã§äœæãããcons
/ car
/ cdr
ãæåã«èŠããšãã if
ã1ã€ãå¿
èŠãªãããšã«é©ããŸããïŒãã ããå
ã®Î»èšç®ã«ãªããšããäºå®ãèãããšãããã¯å¥åŠãªããšã§ã¯ãããŸããïŒã ãã¡ããããã®æ¹æ³ã§ãããè¡ãããã°ã©ãã³ã°èšèªã¯ãããŸããããªããªããããã¯éåžžã«éå¹ççã ããã§ããããããλèšç®ãããã»ã©çŸããããŸããã æç¢ºãªèšèªã§ã¯ããã®ã³ãŒãã¯æ¬¡ã®ããšãè¡ããŸãã
cons
颿°ã¯2ã€ã®å€ïŒ a
ããã³b
ïŒãåããããããä¿æãã颿°ãè¿ããŸãã ãã®é¢æ°ã¯ããã¢ã®ãŸãã«ãªããžã§ã¯ãã§ãã 圌女ã¯åŒæ°ãåãããã¢ã®äž¡æ¹ã®å€ã«å¯ŸããŠåŒã³åºããŸããcar
颿°ã¯ãæž¡ãããåŒæ°ãåŒã³åºããæåã®åŒæ°ãè¿ã颿°ãæž¡ããŸããcdr
颿°ã¯cdr
颿°ãšåãã§ãããå¯äžã®éãã¯ãæž¡ããã颿°ã2çªç®ã®åŒæ°ãè¿ãããšã§ããNIL
颿°ã¯cons
ãšåãããã«æ©èœããŸãããNILã«çãã2ã€ã®å€ãæã€ãã¢ãè¿ããŸãã
cons = λ(a, b) λ(f) f(a, b); car = λ(cell) cell(λ(a, b) a); cdr = λ(cell) cell(λ(a, b) b); NIL = λ(f) f(NIL, NIL); x = cons(1, cons(2, cons(3, cons(4, cons(5, NIL))))); println(car(x));
ãªã¹ãã«ã¯ãååž°çã«å®è£
ã§ããè«ççã«èŠããå€ãã®ã¢ã«ãŽãªãºã ããããŸãã ããšãã°ã次ã®é¢æ°ã¯ããªã¹ãã¢ã€ãã ããšã«æž¡ããã颿°ãåŒã³åºããŸãã
foreach = λ(list, f) if list != NIL { f(car(list)); foreach(cdr(list), f); }; foreach(x, println);
ãããŠãããã«çªå·ã®ç¯å²ã®ãªã¹ããäœæããå¥ã®ãã®ããããŸãïŒ
range = λ(a, b) if a <= b then cons(a, range(a + 1, b)) else NIL;
äžèšã§å®è£
ãããªã¹ãã¯äžå€ã§ãïŒãªã¹ãã®äœæåŸã«cdr
ãŸãã¯cdr
倿Žããããšã¯ã§ããŸããïŒã ã»ãšãã©ã®Lispã«ã¯ããã¢ã倿Žããæ©èœããããŸãã Schemeã§ã¯ããããã¯set-car!
ãšåŒã°set-car!
/ set-cdr!
ã Common Lispã§ã¯ã rplaca
/ rplacd
ã ä»åã¯ãSchemeã®ååã䜿çšããŸãã
cons = λ(x, y) λ(a, i, v) if a == "get" then if i == 0 then x else y else if i == 0 then x = v else y = v; car = λ(cell) cell("get", 0); cdr = λ(cell) cell("get", 1); set-car! = λ(cell, val) cell("set", 0, val); set-cdr! = λ(cell, val) cell("set", 1, val);
ããã¯ãå¯å€ããŒã¿æ§é ãå®è£
ã§ããããšã瀺ããŠããŸãã ç§ã¯ãããã©ã®ããã«æ©èœãããã«ã€ããŠæ·±ãã¯è¿°ã¹ãŸããããããã¯ã³ãŒãããæããã§ãã
ããã«é²ãã§ãªããžã§ã¯ããå®è£
ããããšãã§ããŸãããæ§æã倿Žããªããšå®è¡ãå°é£ã«ãªããŸãã å¥ã®æ¹æ³ã¯ãããŒã¯ãã€ã¶ãŒ/ããŒãµãŒã«æ°ããæ§æãå®è£
ããã€ã³ã¿ãŒããªã¿ãŒã«åŠçã远å ããããšã§ãã ãã¹ãŠã®äž»èŠãªããã°ã©ãã³ã°èšèªããããè¡ããéåžžã®ããã©ãŒãã³ã¹ãéæããå¿
èŠããããŸãã èšäºã®æ¬¡ã®éšåã§æ°ããæ§æã远å ããŸãã
[翻蚳è
ããïŒã©ã ãèšç®ã«èå³ããããªãããã®ãããã¯ã«ç¹åããHabréã®ã¯ãŒã«ãªèšäºããããŸãïŒ JavaScriptã®ã©ã ãèšç® ]ã
æ°ããæ§ææ§æ
λèšèªã«ã¯ãããªãã®æ§ææ§é ããããŸãã ããšãã°ãæ°ãã倿°ãçŽæ¥è¿œå ããæ¹æ³ã¯ãããŸããã åã«ãèšã£ãããã«ãIIFEã䜿çšããå¿
èŠããããããæ¬¡ã®ããã«ãªããŸãã
(λ(x, y){ (λ(z){
let
ããŒã¯ãŒãã远å ããŸãã ããã«ãããæ¬¡ã®ãããªèšè¿°ãå¯èœã«ãªããŸãã
let (x = 2, y = 3, z = x + y) print(x + y + z);
let
ãããã¯å
ã®å倿°ã«ã€ããŠãåããããã¯ããã§ã以åã®å€æ°ãå©çšå¯èœã§ãªããã°ãªããŸããã ãããã£ãŠãäžèšã®ã³ãŒãã¯æ¬¡ã®ã³ãŒããšåçã«ãªããŸãã
(λ(x){ (λ(y){ (λ(z){ print(x + y + z); })(x + y); })(3); })(2);
ãããã®å€æŽã¯ãããŒãµãŒã§çŽæ¥è¡ãããšãã§ããã€ã³ã¿ãŒããªã¿ãŒã§ã®å€æŽã¯äžèŠã§ãã æ°ããlet
ããŒãã远å ãã代ããã«ããããcall
ããŒããšlambda
å€ããããšãã§ããŸãã ããã¯ãèšèªã«æå³çãªå€æŽãå ããªãã£ãããšãæå³ããŸã-ããã¯ãæ§æç³ããšåŒã°ããããã以åã«ååšããASTããŒãã«å€æããæäœã¯ãã·ã¥ã¬ãŒã¬ã¹ãïŒå
ïŒãè±ç³ãïŒãšåŒã°ããŸãã
ãã ãããšã«ããããŒãµãŒã倿Žããå¿
èŠããããŸãã ããå¹ççã«è§£éã§ãããããæ°ãããletãããŒãã远å ããŸãããïŒã¯ããŒãžã£ãŒãäœæããŠããã«åŒã³åºãå¿
èŠã¯ãªããã³ã³ããã¹ããã³ããŒããŠå€æŽããã ãã§ãïŒã
ãŸããSchemeã«ãã£ããlet namedãã®ãµããŒãã远å ããŸãã ã«ãŒãã®äœæãç°¡åã«ãªããŸãã
print(let loop (n = 10) if n > 0 then n + loop(n - 1) else 0);
ããã¯ã10 + 9 + ... + 0ã®åèšãã«ãŠã³ããããååž°çãªãã«ãŒãã§ãã以åã¯ã次ã®ããã«ããå¿
èŠããããŸããã
print((λ(loop){ loop = λ(n) if n > 0 then n + loop(n - 1) else 0; loop(10); })());
ãŸãããããç°¡åã«ããããã«ããååä»ã颿°ãã®æ§æã远å ããŸãã æ¬¡ã®ããã«ãªããŸãã
print((λ loop (n) if n > 0 then n + loop(n - 1) else 0) (10));
ãã®ããã«è¡ãå¿
èŠããã倿ŽïŒ
lambda
ããŒã¯ãŒãã®åŸã®ãªãã·ã§ã³åã®ãµããŒãã ååšããå Žåã¯ã颿°èªäœãæã倿°ãçŸåšã®ã³ã³ããã¹ãã«è¿œå ããå¿
èŠããããŸãã ããã¯ãJavaScriptã§ååãæã€é¢æ°ãšãŸã£ããåãã§ãã- æ°ãã
let
ããŒã¯ãŒãã®ãµããŒãã æ¬¡ã«ããªãã·ã§ã³ã®ååãšãã³ã³ãã§åºåãããfoo = EXPRESSION
ãšãã圢åŒã®å€æ°å®çŸ©ã®ãªã¹ãïŒç©ºã®å ŽåããããŸãïŒãæ¥ãŸãã let
åŒã®æ¬äœã¯åäžã®åŒã§ãïŒãã¡ãããäžé£ã®åŒã«ããããšãã§ããŸãïŒã
ããŒãµãŒã®å€æŽ
æåã«ãããŒã¯ãã€ã¶ãŒã®å°ããªå€æŽã let
ããŒã¯ãŒããããŒã¯ãŒãã®ãªã¹ãã«è¿œå ããŸãã
var keywords = " let if then else lambda λ true false ";
ãªãã·ã§ã³ã®ååãèªã¿åãparse_lambda
颿°ã倿ŽããŸãã
function parse_lambda() { return { type: "lambda", name: input.peek().type == "var" ? input.next().value : null,
次ã«ã let
åŒãè§£æãã颿°ã远å ããŸãã
function parse_let() { skip_kw("let"); if (input.peek().type == "var") { var name = input.next().value; var defs = delimited("(", ")", ",", parse_vardef); return { type: "call", func: { type: "lambda", name: name, vars: defs.map(function(def){ return def.name }), body: parse_expression(), }, args: defs.map(function(def){ return def.def || FALSE }) }; } return { type: "let", vars: delimited("(", ")", ",", parse_vardef), body: parse_expression(), }; }
ããã¯2ã€ã®ã±ãŒã¹ãåŠçããŸãã let
åŸã«var
ããŒã¯ã³ãããå Žåãããã¯ååä»ãã®let
ã§ãã ããã«ãæ¬åŒ§ã§å²ãŸããã«ã³ãã§åºåãããŠããããã delimited
颿°ã䜿çšããŠå®çŸ©ã®ãªã¹ããèªã¿åãã parse_vardef
瀺ãparse_vardef
颿°ã䜿çšããŸãã æ¬¡ã«ãã¿ã€ãcall
ããŒããè¿ãcall
ãããã¯ãïŒIIFEïŒãšããååã®é¢æ°ãããã«åŒã³åºããŸãã 颿°ã®åŒæ°ã¯let
ã§å®çŸ©ããã倿°ã§ããã call
ããŒãã¯å€ãåŒæ°ãšããŠæž¡ããŸãã ãããŠããã¡ããã颿°ã®æ¬äœã¯parse_expression()
ã䜿çšããŠparse_expression()
ãŸãã
ãããåçŽãªlet
ã§ããå Žåã vars
ãã£ãŒã«ããšbody
ãã£ãŒã«ããæã€let
åã®ããŒããè¿ããŸãã vars
ãã£ãŒã«ãã«ã¯ã次ã®åœ¢åŒã®å€æ°ã®é
åãå«ãŸããŸãïŒ { name: VARIABLE, def: AST }
ããããã¯æ¬¡ã®é¢æ°ã«ãã£ãŠè§£æãããŸãïŒ
function parse_vardef() { var name = parse_varname(), def; if (is_op("=")) { input.next(); def = parse_expression(); } return { name: name, def: def }; }
ãŸããæ°ããã¿ã€ãã®åŒã®ãã§ãã¯ãparse_atom
颿°ã«parse_atom
ãŸãã
éèš³ã®å€æŽ
ASTã®æ§é ãå€ãã¿ã€ãã®ããŒãã«ãã¯ã©ããã³ã°ããã代ããã«å€æŽããããšã«ãããããã€ã³ã¿ãŒããªã¿ãŒã«æ°ããããžãã¯ã®åŠçã远å ããå¿
èŠããããŸãã
ãªãã·ã§ã³ã®é¢æ°åã®ãµããŒãã远å ããã«ã¯ã make_lambda
颿°ã倿ŽããŸãã
function make_lambda(env, exp) { if (exp.name) {
颿°ã«ååãããå Žåãã¯ããŒãžã£ãŒãäœæãããšãã«ãã³ã³ããã¹ãã®ã³ããŒãäœæãã颿°ãã³ã³ããã¹ãã«è¿œå ããŸãã æ®ãã¯åããŸãŸã§ãã
æåŸã«ã let
åã®ããŒããåŠçããããã«ãã€ã³ã¿ãŒããªã¿ãŒã«æ¬¡ã®ã±ãŒã¹ã远å ããŸãã
case "let": exp.vars.forEach(function(v){ var scope = env.extend(); scope.def(v.name, v.def ? evaluate(v.def, env) : false); env = scope; }); return evaluate(exp.body, env);
å倿°ã«å¯ŸããŠãæ°ãã倿°ã远å ãããæ°ããã³ã³ããã¹ããäœæãããããšã«æ³šæããŠãã ããã ãã®åŸãæåŸã®ã³ã³ããã¹ãã§æ¬äœãå®è¡ããã ãã§ãã
äŸ
println(let loop (n = 100) if n > 0 then n + loop(n - 1) else 0); let (x = 2, y = x + 1, z = x + y) println(x + y + z);
â .
. , , , . JavaScript λ.
:
globalEnv.def("fibJS", function fibJS(n){ if (n < 2) return n; return fibJS(n - 1) + fibJS(n - 2); }); globalEnv.def("time", function(fn){ var t1 = Date.now(); var ret = fn(); var t2 = Date.now(); println("Time: " + (t2 - t1) + "ms"); return ret; });
time
, , , .
fib = λ(n) if n < 2 then n else fib(n - 1) + fib(n - 2); print("fib(10): "); time( λ() println(fib(10)) ); print("fibJS(10): "); time( λ() println(fibJS(10)) ); println("---");
, Google Chrome, n (27), λ , , JS 4 . , , .
λ JavaScript. , for
/ while
; JS. ? JS , .
, , JavaScript, , JavaScript.
, , . , .
ãããã«
, . , - ; , , ? â JavaScript. , JavaScript â ? , , JavaScript, , , . JavaScript ( , ).
, , Lisp â : //. , , .. Lisp . Lisp let
, , Lisp.
: JavaScript. 3: CPS-