LISP宏,可在运行时处理内部的变量和数据结构

时间:2019-04-25 20:43:16

标签: macros scheme lisp common-lisp lisp-macros

我有用JavaScript编写的LISP(https://jcubic.github.io/lips/,带有在线演示,您可以在其中试用),并且我有这样的宏:

(define-macro (globalize symbol)
  (let ((obj (--> (. lips 'env) (get symbol))))
    `(begin
       ,@(map (lambda (key)
                (print (concat key " " (function? (. obj key))))
                (if (function? (. obj key))
                    (let* ((fname (gensym))
                           (args (gensym))
                           (code `(define (,(string->symbol key) . ,args)
                                    (apply (. ,obj ,key) ,args))))
                      (print code)
                      code)))
              ;; native Object.key function call on input object
              (array->list (--> Object (keys obj)))))))

在此代码中,我使用以下代码:

(let ((obj (--> (. lips 'env) (get symbol))))

我使用以下命令调用此宏:

(globalize pfs)

为pfs的每个静态方法创建函数(这是来自isomorphic-git的LightingFS,其中每个函数返回一个promise,就像来自节点的fs)。

但它不适用于以下情况:

(let ((x pfs))
  (globalize x))

因为lips.env是全球环境。

所以我的问题是宏应该如何工作?他们应该只将输入数据作为符号来处理,以便在评估Lisp代码之前从未访问过对象吗?

基于变量生成一堆函数的LISP宏应该是什么样子。例如在方案中,如果我在变量中有一个列表,并且希望为每个将返回值的键生成函数:

输入:

(define input `((foo . 10) (bar . 20)))

输出:

(begin
  (define (foo) 10)
  (define (bar) 20))

如果使用(macro input),我可以编写宏来提供这样的输出吗?还是唯一的选择是(macro ((foo . 10) (bar . 20)))

我可以接受通用的Scheme或Common LISP答案,但是请不要从scheme中发布define-syntax和卫生宏,我的lisp没有它们,也永远不会。

问题似乎是我想在宏扩展时访问值,并且它需要在运行时具有该值。第二个问题是在这种情况下 eval 是唯一的选择吗?

这在biwascheme中有效:

(define-macro (macro obj)
  (let ((obj (eval obj)))
    `(begin
       ,@(map (lambda (pair)
                (let ((name (car pair))
                      (value (cdr pair)))
                `(define (,name) ,value)))
              obj))))

(define input `((foo . 10) (bar . 20)))

(macro input)

(foo)
;; ==> 10
(bar)
;; ==> 20

(在我的口齿不清中,它的工作方式不同于biwascheme,但这是另一个问题)。

但是这不起作用,因为x不是全局的:

(let ((x '((g . 10)))) (macro x))

带有eval的宏是您通常会执行的操作,还是应该避免使用它们?还有其他方法可以根据运行时对象生成函数束。

1 个答案:

答案 0 :(得分:2)

在Common Lisp中:在运行时创建和编译函数。

select year,
       sum(case when systemtype = 'handled' then 1 else 0 end) handled,
       sum(case when systemtype = 'console' then 1 else 0 end) console
from systems
where Year is not null
group by Year

通过局部变量:

CL-USER 20 > (defparameter *input* '((foo . 10) (bar . 20)))
*INPUT*

CL-USER 21 > (defun make-my-functions (input)
               (loop for (symbol . number) in input
                     do (compile symbol `(lambda  () ,number))))
MAKE-MY-FUNCTIONS

CL-USER 22 > (make-my-functions *input*)
NIL

CL-USER 23 > (foo)
10

CL-USER 24 > (bar)
20

有了宏,更加笨拙和有限:

CL-USER 25 > (let ((input '((foo2 . 102) (bar3 . 303))))
               (make-my-functions input))
NIL

CL-USER 26 > (bar3)
303