Python data structures to lisp
Posted May 16, 2015 at 10:47 AM | categories: lisp, emacs, python | tags:
I have an idea in mind that would use the output of python scripts in lisp functions. Xah Lee posted an idea for writing emacs commands in scripting languages . In this post I want to explore an extension of the idea, where a Python script will return output that can be read in Lisp, e.g. we can convert a Python list to a lisp list, or a dictionary to an a-list or p-list. I can already see that simple data structures will be "simple", and arbitrary data structures will offer a lot of challenges, e.g. nested lists or dictionaries…
If I could add some custom functions to the basic builtin types in Python, then I could use another approach to format python objects as lisp data types. This isn't recommended by Pythonistas, but I guess they don't want to use lisp as much as I do ;) I found this approach to modifying builtins:
We use that almost verbatim here to get what I want. This is a super low level way to add functions to the builtins. I add some simple formatting to floats, ints and strings. I add a more complex recursive formatting function to lists, tuples and dictionaries. A dictionary can be represented as an alist or plist. Both examples are shown, but I leave the alist version commented out. Finally, we add a lispify function to numpy arrays.
import ctypes as c class PyObject_HEAD(c.Structure): _fields_ = [('HEAD', c.c_ubyte * (object.__basicsize__ - c.sizeof(c.c_void_p))), ('ob_type', c.c_void_p)] _get_dict = c.pythonapi._PyObject_GetDictPtr _get_dict.restype = c.POINTER(c.py_object) _get_dict.argtypes = [c.py_object] def get_dict(object): return _get_dict(object).contents.value get_dict(str)['lisp'] = lambda s:'"{}"'.format(str(s)) get_dict(float)['lisp'] = lambda f:'{}'.format(str(f)) get_dict(int)['lisp'] = lambda f:'{}'.format(str(f)) import collections import numpy as np def lispify(L): "Convert a Python object L to a lisp representation." if (isinstance(L, str) or isinstance(L, float) or isinstance(L, int)): return L.lisp() elif (isinstance(L, list) or isinstance(L, tuple) or isinstance(L, np.ndarray)): s = [] for element in L: s += [element.lisp()] return '(' + ' '.join(s) + ')' elif isinstance(L, dict): s = [] for key in L: # alist format # s += ["({0} . {1})".format(key, L[key].lisp())] # plist s += [":{0} {1}".format(key, L[key].lisp())] return '(' + ' '.join(s) + ')' get_dict(list)['lisp'] = lispify get_dict(tuple)['lisp'] = lispify get_dict(dict)['lisp'] = lispify get_dict(np.ndarray)['lisp'] = lispify
Let us test these out.
from pylisp import * a = 4.5 print int(a).lisp() print a.lisp() print "test".lisp() print [1, 2, 3].lisp() print (1, 2, 3).lisp() print [[1, 3], (5, 6)].lisp() print {"a": 5}.lisp() print [[1, 3], (5, 6), {"a": 5, "b": "test"}].lisp() A = np.array([1, 3, 4]) print A.lisp() print ({"tree": [5, 6]}, ["a", 4, "list"], 5, 2.0 / 3.0).lisp()
4 4.5 "test" (1 2 3) (1 2 3) ((1 3) (5 6)) (:a 5) ((1 3) (5 6) (:a 5 :b "test")) (1 3 4) ((:tree (5 6)) ("a" 4 "list") 5 0.666666666667)
Now, is that better than a single lisp function with a lot of conditionals to handle each type? I am not sure. This seems to work pretty well.
Here is how I imagine using this idea. We would have some emacs-lisp variables and use them to dynamically generate a python script. We run the python script, capturing the output, and read it back in as a lisp data structure. Here is a simple kind of example that generates a dictionary.
(let* ((elisp-var 6) (result) (script (format " from pylisp import * print {x: [2*y for y in range(x)] for x in range(1, %s)}.lisp() " elisp-var))) ;; start a python process (run-python) (setq result (read (python-shell-send-string-no-output script))) (plist-get result :5))
(0 2 4 6 8)
That seems to work pretty well. One alternative idea to this is Pymacs , which I have written about before . This project isn't currently under active development, and I ran into some difficulties with it before.
Here we can solve the problem I previously posed and get the result back as an elisp float, and then reuse the result
(let* ((myvar 3) (script (format "from pylisp import * from scipy.optimize import fsolve def objective(x): return x - 5 ans, = fsolve(objective, %s) print ans.lisp()" myvar))) (run-python) (setq result (read (python-shell-send-string-no-output script))) (- 5 result))
0.0
Bottom line: we can write python code in lisp functions that are dynamically updated, execute them, and get lisp data structures back for simple data types. I think that could be useful in some applications, where it is easier to do parsing/analysis in Python, but you want to do something else that is easier in Lisp.
Copyright (C) 2015 by John Kitchin. See the License for information about copying.
Org-mode version = 8.2.10