代码之家  ›  专栏  ›  技术社区  ›  Greg Nisbet

Common Lisp为宏中使用的符号添加后缀

  •  1
  • Greg Nisbet  · 技术社区  · 7 年前


    somesymbol-realpart somesymbol-i .

    我希望能够让下面的代码片段正常工作。

    (let
         ((my-quat '(1 2 3 4)))
      (with-quaternion my-quat
               (assert-equalp 1 my-quat-realpart)
               (assert-equalp 2 my-quat-i)
               (assert-equalp 3 my-quat-j)
               (assert-equalp 4 my-quat-k)))
    

    但是,我用于生成带有后缀的符号的方法似乎会生成带有转义大写字符的奇怪的区分大小写的符号。

    (defun add-suffix-to-symbol (sym suffix)
      (intern (concatenate 'string "" (string sym) "-" suffix)))
    

    intern 出于某种原因保留了该情况,因此我必须执行以下操作来引用 with-quaternion

    (let
        ((my-quat '(1 2 3 4)))
      (with-quaternion my-quat
               (assert-equalp 1 |MY-QUAT-realpart|)
               (assert-equalp 2 |MY-QUAT-i|)
               (assert-equalp 3 |MY-QUAT-j|)
               (assert-equalp 4 |MY-QUAT-k|)))
    

    (defun assert-equalp (e a)
      (assert (equalp e a)))
    
    (defun quat-realpart (q)
      (first q))
    
    (defun quat-i (q)
      (second q))
    
    (defun quat-j (q)
      (third q))
    
    (defun quat-k (q)
      (fourth q))
    
    (assert-equalp '1 (quat-realpart '(1 2 3 4)))
    (assert-equalp '2 (quat-i '(1 2 3 4)))
    (assert-equalp '3 (quat-j '(1 2 3 4)))
    (assert-equalp '4 (quat-k '(1 2 3 4)))
    
    (defun add-suffix-to-symbol (sym suffix)
      (intern (concatenate 'string "" (string sym) "-" suffix)))
    
    (print (add-suffix-to-symbol 'a "suffix"))
    
    (defgeneric with-quaternion-impl (q-sym body))
    
    (defmethod with-quaternion-impl ((q-sym symbol) body)
      (let
          ((q-realpart (add-suffix-to-symbol q-sym "realpart"))
           (q-i (add-suffix-to-symbol q-sym "i"))
           (q-j (add-suffix-to-symbol q-sym "j"))
           (q-k (add-suffix-to-symbol q-sym "k")))
        `(let
         ((,q-realpart (quat-realpart ,q-sym))
          (,q-i (quat-i ,q-sym))
          (,q-j (quat-j ,q-sym))
          (,q-k (quat-k ,q-sym)))
           (progn ,@body))))
    
    
    (defmacro with-quaternion (q-sym &rest body)
      (with-quaternion-impl q-sym body))
    
    
    (let
        ((my-quat '(1 2 3 4)))
      (with-quaternion my-quat
               (assert-equalp 1 |MY-QUAT-realpart|)
               (assert-equalp 2 |MY-QUAT-i|)
               (assert-equalp 3 |MY-QUAT-j|)
               (assert-equalp 4 |MY-QUAT-k|)))
    
    (let
        ((my-quat '(1 2 3 4)))
      (with-quaternion my-quat
               (assert-equalp 1 my-quat-realpart)
               (assert-equalp 2 my-quat-i)
               (assert-equalp 3 my-quat-j)
               (assert-equalp 4 my-quat-k)))
    

    当遇到 clisp ,它将打印以下符号,明显带有转义大写字符。

    |A-suffix|
    

    并生成以下错误消息:

    *** - PROGN: variable MY-QUAT-REALPART has no value
    
    2 回复  |  直到 7 年前
        1
  •  6
  •   Joe Taylor    7 年前

    公共lisp中的符号默认为大写。明显的大小写不敏感是因为您键入的所有内容在读取/检索时都转换为大写,除非您使用带有条形字符的特殊语法 |My-case-sensitive-SYMBOL| my-case-insensitive-symbol MY-CASE-INSENSITIVE-SYMBOL 引用相同的插入符号,该符号以全大写形式存储(尽管这是常见的lisp,但通常可以使用命令行选项和读卡器宏来更改)。该符号实际上根本不区分大小写,它只是看起来是这样的,因为代码中的大多数符号都是读卡器大写的,除非您通过将它们包围在条形字符中或故意配置具有不寻常读卡器选项的环境来特别豁免它们。

    上述所有操作的最终效果是,如果您想使用更熟悉的语法访问宏生成的符号,请确保在插入之前将所有组件都大写,例如:

    (add-suffix-to-symbol q-sym "I")
    

    (add-suffix-to-symbol q-sym "i")
    

    另一个选项是传递要连接的符号,而不是字符串,例如。

    (defun add-suffix-to-symbol (sym suffix)
      (intern (concatenate 'string "" (string sym) "-" (string suffix))))
    
    (print (add-suffix-to-symbol 'FOO 'bar)) ; foo-bar
    (print (add-suffix-to-symbol 'foo '|bar|)) ; |FOO-bar| because foo is converted to FOO at read time
    
        2
  •  3
  •   Rainer Joswig mmmmmm    7 年前

    要对Joe的回答补充一点:

    符号是一种具有名称、值和属性列表的数据类型,它们可以存放在包(另一种Lisp数据结构)中。

    符号保留其名称字符串大小写

    您可以从字符串创建名称为的符号,也可以询问符号的名称。制作符号的功能是, make-symbol :

    CL-USER 8 > (make-symbol "This is A Symbol!!!***")
    #:|This is A Symbol!!!***|
    
    CL-USER 9 > (symbol-name (make-symbol "This is A Symbol!!!***"))
    "This is A Symbol!!!***"
    

    为读取器转义符号

    | 还是单身 \ :

    CL-USER 11 > '|foo BAR ***# <>|
    |foo BAR ***# <>|
    
    CL-USER 12 > '\f\o\o\ BAR\ ***#\ <>
    |foo BAR ***# <>|
    

    Lisp读取器可以使用以下函数 find-symbol intern 查找或创建符号。两者都可以将字符串作为输入,并且区分大小写:

    CL-USER 15 > (let ((symbol '|foo|))
                   (eq (find-symbol "FOO") symbol))
    NIL
    

    但是阅读器本身(例如通过 read read-from-string )默认情况下不区分大小写。默认情况下,所有符号都大写:

    CL-USER 21 > (symbol-name 'foo)
    "FOO"
    
    CL-USER 22 > (symbol-name 'FOO)
    "FOO"
    
    CL-USER 23 > (eq 'foo 'FOO)
    T
    

    CL-USER 35 > *print-case*
    :UPCASE
    
    CL-USER 36 > (readtable-case *readtable*)
    :UPCASE
    

    在宏中创建符号时,我们通常需要大写字符串

    小写字母:

    CL-USER 25 > (intern "zippy")
    |zippy|
    NIL
    

    大写:

    CL-USER 26 > (intern "ZIPPY")
    ZIPPY
    NIL
    

    在数据中,我们有时需要大小写混合的符号:转义它们

    有时我们需要处理不同的案例:例如,当案例需要保留,因为它被用作数据时:

    CL-USER 27 > (defvar *parents* '(|Eva Luator| |Ben BitDiddle jr.|))
    *PARENTS*
    
    CL-USER 28 > *parents*
    (|Eva Luator| |Ben BitDiddle jr.|)
    

    使用格式创建大写符号名称

    通常,在代码中,使用 format -这可能比 concatenate 格式 控件字符串,其中文本或其部分通过使用 ~:@( ~) :

    CL-USER 33 > (format nil "~:@(~a-~a-~a~)" "my" "macro" "name")
    "MY-MACRO-NAME"
    
    CL-USER 34 > (intern (format nil "~:@(~a-~a-~a~)" "my" "macro" "name"))
    MY-MACRO-NAME