在 Common Lisp 中使用宏生成 TYPECASE [英] Generate TYPECASE with macro in Common Lisp

查看:14
本文介绍了在 Common Lisp 中使用宏生成 TYPECASE的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含两个元素子列表的列表,它们将在程序过程中发生变化和增长.我想编写一个宏,它需要一个键并动态生成一个 case,例如:

I have a list of two element sublists which will change and grow in the course of the program. I want to write a macro which takes a key and generates a case dynamically like:

;; This is the List for saving CASE clauses
(setf l '((number 2) (symbol 3)))

;; and i want to have the following expansion
(typecase 'y
  (number 2)
  (symbol 3)) 

我可以有一个只引用全局 l:

I could have a macro which only refers to the global l:

(defmacro m (x)
  `(typecase ,x ,@l))

会正确展开

(m 'y)  ;expands to (TYPECASE 'Y (number 2) (symbol 3))

但是我怎样才能为列表 l 编写带有参数的宏,以便它也可以与其他列表一起使用?

But how can i write the macro with a parameter for the list l so that it would work with other lists as well?

;; A macro which should generate the case based on the above list
(defmacro m (x l)
  `(typecase ,x ,@l))

这不起作用,因为参数列表中的 l 是符号,并且对 (m 'yl) 的调用将扩展为 (TYPECASE 'Y .L).

This doesn't work since l in the arguments list i a symbol and a call to (m 'y l) will expand to (TYPECASE 'Y . L).

想坚持typecase机制,我的解决方法如下:

Wanting to adhere to typecase mechanism, my workaround was as follows:

(setf types-x '(((integer 0 *) 38)
                ((eql neli) "Neli in X")
                (symbol 39))
            )
(setf types-y '(((eql neli) "Neli in Y")
                ((array bit *) "A Bit Vector")))

(defmacro m (x types-id)
  (case types-id
    (:x `(typecase ,x ,@types-x))
    (:y `(etypecase ,x ,@types-y))))

(m 'neli :x)                ;"Neli in X"
(m 'neli :y)                ;"Neli in Y"
(m 'foo :x)             ;39

感谢任何提示和评论.

推荐答案

你不需要宏来做你想做的事:使用函数.

You don't need a macro for what you're trying to do: use a function.

例如,给定

(defvar *type-matches*
  '((float 0)
    (number 1)
    (t 3)))

然后

(defun type-match (thing &optional (against *type-matches*))
  (loop for (type val) in against
        when (typep thing type)
        return (values val type)
        finally (return (values nil nil))))

将事物与类型匹配:

> (type-match 1.0)
0
float

> (type-match 1)
1
number

您希望保持变量按类型排序,例如:

You want to keep the variables sorted by type, which you can do by, for instance:

(setf *type-matches* (sort *type-matches* #'subtypep :key #'car))

您当然希望保持匹配排序.

You want to keep the matches sorted of course.

如果你想延迟表单的执行,那么你可以这样做(这也涉及对类型进行排序):

If you want to delay the execution of the forms then you can do something like this (this also deals with sorting the types):

(defvar *type-matches*
  '())

(defmacro define-type-match (type/spec &body forms)
  ;; define a type match, optionally in a specified list
  (multiple-value-bind (type var)
      (etypecase type/spec
        (symbol (values type/spec '*type-matches*))
        (cons (values (first type/spec) (second type/spec))))
    (let ((foundn (gensym "FOUND")))
      `(let ((,foundn (assoc ',type ,var :test #'equal)))
         (if ,foundn
             (setf (cdr ,foundn) (lambda () ,@forms))
           (setf ,var (sort (acons ',type (lambda () ,@forms) ,var)
                            #'subtypep :key #'car)))
         ',type/spec))))

(defun type-match (thing &optional (against *type-matches*))
  (loop for (type . f) in against
        when (typep thing type)
        return (values (funcall f) type)
        finally (return (values nil nil))))

这篇关于在 Common Lisp 中使用宏生成 TYPECASE的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆