XST 45: Moar Scheming
Just a bit more playing with the Lispy Calculator to while away a few minutes in the afternoon.
I believe that I’m only missing the if
function from my eval, along with a lot of functions that show up in the environment. Let’s create a test.
_:test("if", function()
local prog = "(if (< 5 10) 10 20)"
local result = LISP:eval(LISP:parse(prog))
_:expect(result).is(10)
end)
This is going to barf on the if
and on the <
.
9: if -- LISPCalc:53: No such symbol as Symbol if
elseif x[1].token == 'if' then
_ignored,test,conseq,alt = table.unpack(x)
local exp = self:eval(test,env) and conseq or alt
return self:eval(exp,env)
We evaluate the conditional (test) and then select which of the consequent and alternative to evaluate. if
guarantees to evaluate only the chosen expression.
Now I imagine I’ll find < not to work.
9: if -- LISPCalc:53: No such symbol as Symbol <
So far so good. Put that into the environment.
tab["<"] = function(a,b) return a<b end
I rather imagine that this will work. And it does. Life is good. Extend the test to a few more operators.
prog = "(if (> 5 10) 10 20)"
result = LISP:eval(LISP:parse(prog))
_:expect(result).is(20)
Test will fail for want ot >
. Providing that should make it green. And it does. Commit: if works. Added < and >.
Let’s do some more comparators and such. I feel as if I should test them all.
result = LISP:exec("(if (<= 5 5) 1 0)")
_:expect(result).is(1)
result = LISP:exec("(if (<= 5 4) 1 0)")
_:expect(result).is(0)
result = LISP:exec("(if (>= 5 5) 1 0)")
_:expect(result).is(1)
result = LISP:exec("(if (>= 4 5) 1 0)")
_:expect(result).is(0)
I decided that I need this:
9: if -- Tests:87: attempt to call a nil value (method 'exec')
function LISP:exec(prog)
return self:eval(self:parse(prog))
end
9: if -- LISPCalc:53: No such symbol as Symbol <=
tab[">="] = function(a,b) return a>=b end
tab["<="] = function(a,b) return a<=b end
Tests green. Commit: >= and <=. LISP:exec does parse+eval.
We need =
and eq?
.
result = LISP:exec("(if (= 5 5) 1 0)")
_:expect(result).is(1)
result = LISP:exec("(if (eq? 5 5) 1 0)")
_:expect(result).is(1)
result = LISP:exec("(if (= 5 6) 1 0)")
_:expect(result).is(0)
result = LISP:exec("(if (eq? 5 6) 1 0)")
_:expect(result).is(0)
tab["="] = function(a,b) return a==b end
tab["eq?"] = function(a,b) return a==b end
Commit: = and eq?
A more careful reading tells me that eq?
is identity and equals?
is whatever =
and op.eq are in Python. I’m going to ignore that issue for now.
I’m wondering about apply
, among other things that I find in Norvig’s environment:
import math
import operator as op
def standard_env() -> Env:
"An environment with some Scheme standard procedures."
env = Env()
env.update(vars(math)) # sin, cos, sqrt, pi, ...
env.update({
'+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv,
'>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq,
'abs': abs,
'append': op.add,
'apply': lambda proc, args: proc(*args),
'begin': lambda *x: x[-1],
'car': lambda x: x[0],
'cdr': lambda x: x[1:],
'cons': lambda x,y: [x] + y,
'eq?': op.is_,
'expt': pow,
'equal?': op.eq,
'length': len,
'list': lambda *x: List(x),
'list?': lambda x: isinstance(x, List),
'map': map,
'max': max,
'min': min,
'not': op.not_,
'null?': lambda x: x == [],
'number?': lambda x: isinstance(x, Number),
'print': print,
'procedure?': callable,
'round': round,
'symbol?': lambda x: isinstance(x, Symbol),
})
return env
global_env = standard_env()
I am wondering about List, cons, car, and cdr, not because I don’t know what they are but because I’m not sure what the Python means. Remind me to learn some Python one of these days.
Same with map. Is it mapcar? I think Scheme says map
when Lisp says mapcar
. I think all that wants to wait until tomorrow, when I’ll mess with list
. That should open up what apply
and map
are good for.
I really wish Norvig had provided some tests. It would help someone whose Lisp experience was before most of you were born. I’m a bit rusty.
Anyway, this was fun. See you next time!