【问题标题】:Usefulness/point of function "symbol-name"?功能“符号名称”的用处/要点?
【发布时间】:2019-10-13 17:19:41
【问题描述】:

乍一看,有一个返回符号名称的函数似乎有点愚蠢,必须使用相同符号的名称来调用它。即在调用上下文中函数将返回什么应该已经很明显了。另一方面,identity 有时很有用(我忘了刚​​才在哪里),但我认为(也许是错误的)symbol-function 存在的原因不仅仅是作为一种身份。

不过,hyperspec 提供了一个可能的提示:

 (symbol-name 'temp) =>  "TEMP" 
 (symbol-name :start) =>  "START"
 (symbol-name (gensym)) =>  "G1234" ;for example

我注意到:start 表示从关键字包中获取符号start 的名称,其中关键字包简单地用: 表示。 (:keyword 是它的较长形式,不需要使用)。因此,在这种情况下,symbol-name 扮演着简单删除包前缀的角色。

它可能做的另一件事是,如果实现不区分大小写,它将通过删除提供的符号名称中的大小写来获取实际名称。

大概是这样还是我缺少这个功能有什么重要性?

我对符号感到困惑的一件事(现在澄清了)是symbol-plist 并没有告诉您有关符号的所有信息(例如,它是否包含特殊变量或函数的值)。相反,plist 是一个主要的遗留功能,现在很大程度上被哈希表取代。因此,即使在符号上设置了特殊变量,对 symbol-plist 的调用也会返回 NIL

关于这个问题的最后一个问题,Paul Graham 在第 8 章 (p133) 中说,“您可以将符号用作数据对象和事物的名称,而无需了解两者之间的关系”。如果我们现在很少使用 plist 是否正确,那么今天我们通常根本不使用符号“作为数据对象”,而是只是作为事物的名称(当然在 CL 中具有二元性,即同时使用 symbol-functionsymbol-value)。

【问题讨论】:

  • 符号列表等价于 weak 哈希表

标签: common-lisp


【解决方案1】:

符号是对象。您可以创建它们并以编程方式传递它们。这些对象的属性之一是它们的名称,它是一个字符串。 Symbol-name 是读者(不是访问者)。就是这样。

符号用于表示代码。在这种情况下,它们由阅读器创建并由编译器使用。然而,这不是它们的唯一用途,规范也没有做出这样的假设。我希望这能解决您问题的核心。

这是一个函数,给定一个符号,返回一个在同一个包中但名称相反的符号:

(defun reverse-symbol (symbol)
  (intern (make-symbol (reverse (symbol-name symbol)))
          (symbol-package symbol)))

这是一个函数,给定一个字符串指示符,返回指定的字符串:

(defun designated-string (string-designator)
  (ctypecase string-designator
    (string string-designator)
    (symbol (symbol-name string-designator))))

您还可以在宏和编译器宏中进行各种恶作剧,方法是检查给定形式的符号名称并应用一些约定(不是您应该...)。

【讨论】:

    【解决方案2】:

    假设您想编写一些协议,用于通过某个串行通道在两个不同系统之间传输结构位

    (defgeneric transmit-object (o stream))
    

    符号的方法是什么?

    (defmethod transmit-object ((o symbol) stream)
      ... (symbol-name o) ...)
    

    尤其是在需要推理的时候,您肯定不知道符号的名称,并且如果不使用symbol-name(或一些绝对令人震惊的黑客,例如将符号打印到一个字符串并使用它)。

    关于symbol-plist,实现完全可以在符号的属性列表中保留各种信息。我什至不确定是否允许执行(更聪明的版本):

    (defun symbol-value (s)
      (let* ((d (load-time-value (cons nil nil)))
             (v (get s secret-value-indicator d)))
        (when (eq v d)
          (error ...))
        v))
    

    这可能是不允许的,但我不确定。当然,以这种方式保存函数定义曾经相当普遍。

    【讨论】:

      【解决方案3】:

      处理符号名称并不是那么愚蠢。例如,当您构建预处理器时-您读取一些数据,将其转换为列表结构,然后一些代码正在处理这些列表并在symbol-name 可以说defun-my-ubermacro 时触发一些额外的逻辑。这正是 Graham 引用的内容 - 您将符号视为数据。

      而且(在我看来)说不使用 plist 时通常不使用符号作为数据是不正确的。 Plists 只是在许多地方有用的地方。很多宏所做的是将符号处理为数据/名称。

      【讨论】:

        【解决方案4】:

        乍一看,有一个返回符号名称的函数似乎有点傻,必须使用相同符号的名称来调用它。

        错了。 symbol-name 是用符号而不是名称调用的。它以字符串形式返回名称。

        我注意到 :start 表示从关键字包开始获取符号的名称,其中关键字包简单地用:表示。 (:keyword 是它的较长形式,不需要使用)。因此,在这种情况下,symbol-name 的作用是简单地删除包前缀。

        不,symbol-name 将符号名称作为字符串返回。关键字符号是symbol 类型的对象。

        符号是一种数据类型,具有多个单元

        • 名称,字符串
        • 可能是一个函数
        • 可能是一个值
        • 属性列表
        • (可选)它所在的 home 包

        根本不使用符号“作为数据对象”,而是将其用作事物的名称

        不,符号作为数据对象有很多用途。例如 Lisp 源代码使用了许多符号。但它也可以出现在各种数据中。

        CL-USER 6 > 'foo
        FOO
        
        CL-USER 7 > (type-of 'foo)
        SYMBOL
        
        CL-USER 8 > (symbol-name 'foo)
        "FOO"
        
        CL-USER 9 > (type-of (symbol-name 'foo))
        SIMPLE-BASE-STRING
        
        CL-USER 10 > (mapcar #'symbol-name '(a b c defun foo bar))
        ("A" "B" "C" "DEFUN" "FOO" "BAR")
        
        CL-USER 11 > (mapcar #'type-of *)
        (SIMPLE-BASE-STRING SIMPLE-BASE-STRING SIMPLE-BASE-STRING SIMPLE-BASE-STRING SIMPLE-BASE-STRING SIMPLE-BASE-STRING)
        

        【讨论】:

          【解决方案5】:

          由于您尚未接受答案,因此这是我的看法。

          对于大多数日常编程符号,因此 SYMBOL-NAME, 不是很有用。大多数情况下,它们被用于他们的 独特性。但是,当您修改编译器时它们会发光 使用宏。

          这里有 3 个使用 SYMBOL-NAME 的示例:

          首先,LOOP 是 CL 通用循环结构,它通过以下方式工作 占位符符号以某种方式声称是非口齿不清的,但它 仍然非常方便,尤其是当您并行执行时。

          以下 3 种形式是等效的(在运行时):

          (loop for i in list collect i)
          (loop :for i :in list :collect i)
          (loop #:for i #:in list #:collect i)
          

          我个人更喜欢第三种形式,因为它确实 很明显 LOOP 魔法发生在哪里,也避免了实习 任何包中的符号(通常是无害的,但不是 总是)。第三个作品完全需要存在 符号名称

          其次,我不怎么使用 CLOS,但课程是不可否认的 有用。 DEFCLASS 对于我想做的事情来说往往过于冗长, 所以我经常使用一个使用隐含符号的宏。所以:

          (defbean peer ()
            (id)
            (next
             beats))
          

          变成:

          (defclass peer nil
            ((id :initarg :id :reader peer-id)
             (next :initarg :next :accessor peer-next)
             (beats :initarg :beats :accessor peer-beats)))
          

          节省大量打字。注意插槽名称如何转换为关键字 对于 initargs,以及如何创建读取器和访问器名称。

          有些人不喜欢这种宏,我知道它可能是怎样的 如果你有一个庞大的团队,每个人都在做这件事,那就有问题了 商店(尽管M-.C-c ret 始终可用),但对于 小团队我认为这是使用 lisp 的最佳理由之一, 根据您的实际使用方式对其进行自定义。

          第三,我的sqlite辅助库使用SYMBOL-NAME生成SQL 声明(以及一些更隐含的符号)甚至更多 代码节省:

          (defsqlite-table artists
            (id :auto-key)
            (artist-name :t)
            (sort-artist-name :t :n))
          

          变得相当巨大:

          (progn
           (defparameter +create-artists-sql+
             "CREATE TABLE artists (
            id INTEGER PRIMARY KEY NOT NULL,
             artist_name TEXT NOT NULL,
             sort_artist_name TEXT
          )")
           (defun create-artists-table (pjs-sqlite::db pjs-sqlite::recursive log)
             ###)
           (eval-when (:load-toplevel)
             ###)
           (defun insert-artists
                  (pjs-sqlite::db artist-name sort-artist-name &key (id nil id-p))
             ###)
           (defun update-artists
                  (pjs-sqlite::db id
                   &key (artist-name nil artist-name-p)
                   (sort-artist-name nil sort-artist-name-p))
             ###)
           (defun delete-artists (pjs-sqlite::db id)
             (with-sqlite-statements (pjs-sqlite::db
                                      (pjs-sqlite::stmt
                                       "DELETE FROM artists WHERE id = ?"))
               ###)))
          

          (我删除了很多会分散注意力的代码,但它应该 清楚我是如何将符号映射到 sql 标识符的)

          我希望这能让您对 SYMBOL-NAME 的类型有所了解 可用于。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-07-15
            • 2019-07-01
            • 2020-01-31
            相关资源
            最近更新 更多