Norvig's reboot of Strachey's 1966 checkers program (ab)uses default arguments to map CPL's 1960's era use of let
-binding. Now, there are other stupid tricks one can play with binding in python, but my current favorite involves misuse[0] of comprehensions.
With a bit of effort, we even have letrec
:
print(min( f(6)
for Y0 in [lambda y,f:
lambda *x: f(y(y,f))(*x)]
for f in [Y0(Y0,lambda fac:
lambda n: 1 if n<2 else
n*fac(n-1))]))
Exercise 1: add one line to produce the classic Y
Exercise 2*: modify Y0
to support the classic mutual recursion between even
and odd
[0] or not exactly misuse? cf Wadler's Comprehending Monads (p19,infra):
[the existence of the map from the identity to the list monad] explains a trick occasionally used...
If anyone feels like taking another run at Cleese (a python VM as bare-metal OS*), it might be worth taking advantage of NetBSD rump kernels.
Dave's Stupid Python Tricks (mark II) Fun
: a toy for interpreting
GFM Tables
per McCarthy 1960:
Fun = lambda s: min( globals().update(eval(com(par(s))))
for par in [lambda txt: [[f.strip() for f in pline(l.split('|'))]
for pline in [ lambda xs:
((xs[0],'') if len(xs)==1 else
xs if len(xs)==2 else
xs[1:3] if len(xs)==4 else None) ]
for l in txt.strip().split('\n') ]]
for com in [lambda xs: compile(xs[0],xs[1:])
if xs and None not in xs else '[]'
for nonsep in [lambda s: any(c not in ' -:' for c in s)]
for compile in [lambda hdr,bdy:
'[("{}",(lambda {}:{} None))]'.format(
hdr[0], hdr[1], ' '.join(clause(b)
for b in bdy if nonsep(b[0]+b[1])))
for clause in [lambda b:
'({}) if ({}) else'.format(
b[0], b[1] or 'True')]]])
if __name__ == "__main__":
Fun('''
| fact | n |
|------------:|:----|
| 1 | n<2 |
| n*fact(n-1) | |
''')
print(fact(5))
Fun('''
gcd | x,y
----------:|:---
gcd(x-y,y) | y<x
gcd(x,y-x) | x<y
x |
''')
print(gcd(10,2014))
Stupid Python Tricks (mark III): "barely literate programming".
expand = lambda node,d: ''.join(expand(x,d) if i%2 else x
for i,x in enumerate(d[node][:-1].split('@')))
findcode = lambda s: { rk[1:-1] : ''.join(rv[1:] for rv in rvs)
for ls in [[l+'\n' for l in s.split('\n') if l[:1] in '@>']]
for js in [[i for i,l in enumerate(ls) if l[0]=='@']+[None]]
for i,j in zip(js[:-1],js[1:])
for rk,rvs in [(ls[i],ls[i+1:j])]}
print(':: :: ::\n'.join(
expand(node, findcode(test))
for node in ['k & r','ansi']
for test in [r"""
this is a test of barely-literate programming.
:: :: ::
First, we try an old-timer program:
@k & r
>main(@formal names@)
>@formal declarations@
>{
> printf(@greetings@);
>}
>
Not that we use them in this program (it is,
after all, a constant function, cf. the 'K'
combinator) but for nostalgia value, we have
@formal names
>argc, argv
@formal declarations
>int argc;
>char *argv[];
and, of course, we should specify the value
which the function takes at all arguments:
@greetings
>"Hello, world\n"
:: :: ::
Now, let's rewrite this in a more modern style:
@ansi
>@external declarations@
>
>int main(@inline formal declarations@)
>{
> printf(@greetings@);
> @more boilerplate...@
>}
>
You'll notice that we have an 'int' in there.
That's because once one has more than a few
K of memory to play with during a compile, it
turns out that silently assuming all params
are machine words isn't the best tradeoff.
Of course, now that we've declared that we're
returning an int, we'd probably better do so:
@more boilerplate...
>return 0;
We must declare the argument types as well
as the return types:
@inline formal declarations
>int argc, char **argv, char **envp
As in Pascal, the types are now specified in
with the argument names. One might expect
that we'd need some additional bookkeeping
for printf() as well as main(), but luckily
(if one is not the preprocessor) this is a
matter of including the proper header file.
@external declarations
>#include <stdio.h>
:: :: ::
(to get fancier, one might try piping output
through "tcc -run", but it would probably be
better to fill in the code sketched here and
improve its robustness and input format)
"""]))
Reservoir sampling, AKA deal. (in APL, at 110 baud, that main expression would have been something likeHAND 5?52
)
import random;print(min(
hand(deal(5,range(52)))
for vals,suits in [("A23456789TJQK", "CDHS")]
for hand in [lambda js: " ".join([
vals[v]+suits[s] for j in js
for s,v in [divmod(j,13)]])]
for resv in [lambda rs,xs,k: rs if not xs else
min([resv([r if i!=j else xs[0]
for i,r in enumerate(rs)],
xs[1:],k+1)
for j in [random.randint(0,k)]])]
for deal in [lambda n,xs:resv(xs[:n],xs[n:],n)]
))
print((lambda d:d+repr(d)+"))")('print((lambda d:d+repr(d)+"))")('))