[PYTHON] 7-line interpreter implementation summary

Summary of my work / 4th stage of cheat sheet. I read the following web page and decided to write the first 7-line interpreter (lambda calculus) in Python and other programming languages. Therefore, for the time being, only the Scheme description of the original language and the Python description.

(λ x . x) → ((λ x . x)) ((λ x . x) (λ a . a)) → (λ a . a) (((λ f . f) (λ g . g)) (λ a . a)) → ((λ a . a)) (((λ f . (λ x . (f x))) (λ g . g )) (λ a . a)) → ((λ a . a))

In this article, the lambda symbol uses "/" (instead of "λ").

For convenience of porting work (?), The structure is divided into the following parts for each language. However, in order to avoid duplication with existing functions, ʻeval is the function name of ʻeval7 and ʻapply is the function name of ʻapply7.

Scheme (with explanation of processing contents) (R5RS)

read choice. It lexically analyzes (parses) S-expressions.

gosh> (read (open-input-string "(hoge hage (hige (foo bar)) baz)"))
(hoge hage (hige (foo bar)) baz)

ʻAssq` is OK.

gosh> (assq 'a '((a 10) (b 20) (c 30)))
(a 10)

Evaluation part of the formula. Almost copy and paste of the above Web page. It takes the expression ʻe and the environment ʻenv, retrieves the corresponding value from the environment ʻenv if the expression ʻe is a variable, and returns it, and if the expression ʻe is a single lambda expression, it takes the environment ʻenv as a value. Other than that, it is regarded as a list of the function (car e) and the value (cadr e) given to the function, and each function / value is self-evaluated first (applicative order). Call ʻapply` to apply the function.

(define (eval7 e env)
  (cond ((symbol? e)      (cadr (assq e env)))
        ((eq? (car e) '/) (cons e env))
        (else             (apply7 (eval7 (car  e) env)
                                  (eval7 (cadr e) env)))))

Function application part. Almost copy and paste of the above Web page. Receives the function f and the value x, associates the function variable (cadr (car f)) with the value x, and combines it with the remaining unapplied value(cdr f)as an environment. , Call the expression evaluation part ʻeval together with the function body expression (cddr (car f)) `.

As a precaution for implementation, the right side of the input lambda expression period is automatically the cdr part of the dot pair in Scheme, so for example, f is((/ a. (/ C. d))). The(cd dr (car f)) at the time of) becomes (\ c. D) instead of (. (/ C .d)).

(define (apply7 f x)
  (eval7 (cddr (car f))
         (cons (list (cadr (car f)) x)
               (cdr f))))

Use display as it is.

gosh> (display '((/ a . a)))
((/ a . a))#<undef>
gosh> (display (eval7 (read) '()))
(((/ f . (/ x . (f x))) (/ g . g )) (/ a . a))
((/ a . a))#<undef>

Python(Python 3,Python2)

Here's read_from_tokens () pakuri. Error handling was lost.

def readlist(x):
    xl = x.replace('(', ' ( ').replace(')', ' ) ').split()
    def ret(ts):
        t = ts.pop(0)
        if t == '(':
            r = []
            while ts[0] != ')': r.append(ret(ts))
            ts.pop(0)
            return tuple(r)
        else:
            return t
    return ret(xl)
>>> readlist('(hoge hage (hige (foo bar)) baz)')
('hoge', 'hage', ('hige', ('foo', 'bar')), 'baz')

At first, I thought about using the dictionary type, but it seemed to be troublesome at the time of λ evaluation, so I implemented it assuming the tuple type.

def assoc(k, vl):
    if not vl: return False
    elif vl[0][0] == k: return vl[0]
    else: return assoc(k, vl[1:])
>>> assoc('a', (('a', 10), ('b', 20), ('c', 30)))
('a', 10)

Subscript great success. Scheme (LISP) (car x), (cdr x), (cadr x) is Python's x [0], x [1:], x [1] Classic (usually the opposite).

def eval7(e, env):
    if isinstance(e, str): return assoc(e, env)[1]
    elif e[0] == '/':      return (e,) + env
    else:                  return apply7(eval7(e[0], env), eval7(e[1], env))

The second big success in subscripting. The reason for cdddr =[3:]instead of cddr = [2:] is to remove '.' (See the explanation of Scheme implementation).

def apply7(f, x): return eval7(f[0][3:][0], ((f[0][1], x),) + f[1:])

The reverse of the input. This is truly my own work. The last [: -1] is very technical.

def writelist(x):
    def ret(xs):
        r = ('(')
        for t in xs:
            if isinstance(t, tuple):
                r += ret(t)
            else:
                r = r + t + ' '
        r = r[:-1] + ') '
        return r
    return ret(x)[:-1]
>>> writelist(('a', 'b', ('c', 'd'), (('e', 'f'), ('g', 'h'))))
'(a b (c d) ((e f) (g h)))'

For Python3 (use ʻinput ()`).

>>> print(writelist(eval7(readlist(input()), ())))
(((/ f . (/ x . (f x))) (/ a . a )) (/ b . b))
((/ b . b))

For Python2 (use raw_input ()).

>>> print(writelist(eval7(readlist(raw_input()), ())))
(((/ f . (/ x . (f x))) (/ a . a )) (/ b . b))
((/ b . b))

Remarks

References

Supplementary information about the article

change history

Recommended Posts

7-line interpreter implementation summary
Stackful coroutine implementation summary
Ensemble learning summary! !! (With implementation)
[Line / Python] Beacon implementation memo
Random forest (implementation / parameter summary)
Summary of basic implementation by PyTorch
1D-CNN, 2D-CNN scratch implementation summary by Pytorch
Machine learning algorithm classification and implementation summary