如何将字符串转换为 Common Lisp 中的关键字?

How to transform a string into a keyword in Common Lisp?

我有这个列表:

((":name" "postalCode" ":type" "tel")
 (":name" "firstName" ":value" "Pedro " ":type" "text")
 (":name" "lastName" ":value" "Moyses Delfino" ":type" "text")
 (":name" "email" ":value" "p.delfino01@gmail.com" ":type" "text")
 (":name" "password" ":value" "senha-minha" ":type" "password")
 (":name" "confirmPassword" ":value" "senha-minha" ":type" "password")
 (":name" "cpf" ":value" "117.349.446-41" ":type" "tel")
 (":name" "rg" ":value" "MG1727039" ":type" "tel")
 (":name" "dateOfBirth" ":value" "07/05/1993" ":type" "tel")
 (":value" "female" ":type" "checkbox") (":value" "male" ":type" "checkbox")
 (":value" "31" ":type" "tel") (":value" "98765-4332" ":type" "tel")
 (":value" "31" ":type" "tel") (":value" "3456-7890" ":type" "tel")
 (":value" "on" ":type" "checkbox") (":value" "on" ":type" "checkbox")
 (":value" "on" ":type" "checkbox") (":value" "on" ":type" "checkbox")
 (":name" "login" ":type" "text") (":name" "password" ":type" "password")
 (":name" "login" ":type" "text") (":name" "login" ":type" "text")
 (":name" "password" ":type" "password"))

在语义上,字符串前有关键字,如:name:type。由于之前的数据解析,他们的关键字被伪装了。我想将它们转换为真正的关键字。因此,列表中的第一个元素将从:

(":name" "postalCode" ":type" "tel")

收件人:

(:name "postalCode" :type "tel")

我认为有 一些 方法可以做到这一点。哪种解决方案是解决它的优雅方法?

谢谢。

按照@Barmar 的建议,我构建了:

(defun convert-keyword (string-list)
  (cond ((null string-list) nil)
        ((equal (subseq (first string-list) 0 1) ":")
           (cons (read-from-string (first string-list))
                 (convert-keyword (rest string-list))))
        (t (cons (first string-list)
                 (convert-keyword (rest string-list))))))

哪个returns:

((:TYPE "tel" :NAME "postalCode" :VALUE)
 (:TYPE "text" :NAME "firstName" :VALUE "Pedro ")
 (:TYPE "text" :NAME "lastName" :VALUE "Moyses Delfino")
 (:TYPE "text" :NAME "email" :VALUE "p.delfino01@gmail.com")
 (:TYPE "password" :NAME "password" :VALUE "senha-minha")
 (:TYPE "password" :NAME "confirmPassword" :VALUE "senha-minha")
 (:TYPE "tel" :NAME "cpf" :VALUE "117.349.446-41")
 (:TYPE "tel" :NAME "rg" :VALUE "MG1727039")
 (:TYPE "tel" :NAME "dateOfBirth" :VALUE "07/05/1993")
 (:TYPE "checkbox" :NAME :VALUE "female")
 (:TYPE "checkbox" :NAME :VALUE "male") (:TYPE "tel" :NAME :VALUE "31")
 (:TYPE "tel" :NAME :VALUE "98765-4332") (:TYPE "tel" :NAME :VALUE "31")
 (:TYPE "tel" :NAME :VALUE "3456-7890") (:TYPE "checkbox" :NAME :VALUE "on")
 (:TYPE "checkbox" :NAME :VALUE "on") (:TYPE "checkbox" :NAME :VALUE "on")
 (:TYPE "checkbox" :NAME :VALUE "on") (:TYPE "text" :NAME "login" :VALUE)
 (:TYPE "password" :NAME "password" :VALUE) (:TYPE "text" :NAME "login" :VALUE)
 (:TYPE "text" :NAME "login" :VALUE) (:TYPE "password" :NAME "password" :VALUE))

CL-USER 26 > (defun convert-string-to-keyword (string
                                               &key
                                               (upcase t)
                                               (max-string-length 100))
              (and (<= 2 (length string) max-string-length)
                   (char= (char string 0) #\:)
                   (let ((string1 (subseq string 1)))
                     (when upcase
                       (setf string1 (string-upcase string1)))
                     (values (intern string1 "KEYWORD")))))
CONVERT-STRING-TO-KEYWORD

CL-USER 27 > (convert-string-to-keyword ":foo")
:FOO

CL-USER 28 > (convert-string-to-keyword ":")
NIL

CL-USER 29 > (convert-string-to-keyword ":foo" :upcase nil)
:|foo|

转换键值列表列表:

(defun convert-key-value-lists (lists)
  (loop for list in lists
        collect (loop for (key value) on list by #'cddr
                      collect (convert-string-to-keyword key)
                      collect value)))

我将对数据做一些假设:

  • 数据总是以(key value key value ...)的形式存在。
  • 数据总是格式正确。

让我们一步一步来。

(defvar *data* '((":name" "postalCode" ":type" "tel")
 (":name" "firstName" ":value" "Pedro " ":type" "text")
 (":name" "lastName" ":value" "Moyses Delfino" ":type" "text")
 (":name" "email" ":value" "p.delfino01@gmail.com" ":type" "text")
 (":name" "password" ":value" "senha-minha" ":type" "password")
 (":name" "confirmPassword" ":value" "senha-minha" ":type" "password")
 (":name" "cpf" ":value" "117.349.446-41" ":type" "tel")
 (":name" "rg" ":value" "MG1727039" ":type" "tel")
 (":name" "dateOfBirth" ":value" "07/05/1993" ":type" "tel")
 (":value" "female" ":type" "checkbox") (":value" "male" ":type" "checkbox")
 (":value" "31" ":type" "tel") (":value" "98765-4332" ":type" "tel")
 (":value" "31" ":type" "tel") (":value" "3456-7890" ":type" "tel")
 (":value" "on" ":type" "checkbox") (":value" "on" ":type" "checkbox")
 (":value" "on" ":type" "checkbox") (":value" "on" ":type" "checkbox")
 (":name" "login" ":type" "text") (":name" "password" ":type" "password")
 (":name" "login" ":type" "text") (":name" "login" ":type" "text")
 (":name" "password" ":type" "password")))

第一步是将字符串化的关键字转换为正确的关键字。

(defun parse-keyword (string)
  (intern (string-upcase (string-left-trim ":" string)) :keyword))

CL-USER> (parse-keyword ":name")
:NAME
:EXTERNAL
CL-USER> 

那就够了。我不需要检查字符串是否以 ':' 开头,因为我从数据结构中假设所有奇数值(前三分之一......)都是关键字,我不需要关心偶数值。

第二步是解析单个值列表。

(defun process-a-list-of-kv (kv-list)
  (let ((result nil))
    (alexandria:doplist (k v kv-list)
      (push (parse-keyword k) result)
      (push v result))
    (nreverse result)))

CL-USER> (process-a-list-of-kv (first *data*))
(:NAME "postalCode" :TYPE "tel")
CL-USER> 

它使用 alexandria:doplist 同时迭代两个值(k 和 v)。我将 k 转换为关键字并且不对 v 做任何事情。如果你不打算使用 Alexandria,你可以使用循环来完成它:

(defun process-a-list-of-kv (kv-list)
  (let ((result nil))
    (loop for k in kv-list by 'cddr
          for v-list = (cdr kv-list) then (cddr v-list)
          for v = (first v-list) then (first v-list)
          do (push (parse-keyword k) result)
             (push v result))
    (nreverse result)))

最后处理原始列表:

CL-USER> (mapcar 'process-a-list-of-kv *data*)
((:NAME "postalCode" :TYPE "tel")
 (:NAME "firstName" :VALUE "Pedro " :TYPE "text")
 (:NAME "lastName" :VALUE "Moyses Delfino" :TYPE "text")
 (:NAME "email" :VALUE "p.delfino01@gmail.com" :TYPE "text")
 (:NAME "password" :VALUE "senha-minha" :TYPE "password")
 (:NAME "confirmPassword" :VALUE "senha-minha" :TYPE "password")
 (:NAME "cpf" :VALUE "117.349.446-41" :TYPE "tel")
 (:NAME "rg" :VALUE "MG1727039" :TYPE "tel")
 (:NAME "dateOfBirth" :VALUE "07/05/1993" :TYPE "tel")
 (:VALUE "female" :TYPE "checkbox") (:VALUE "male" :TYPE "checkbox")
 (:VALUE "31" :TYPE "tel") (:VALUE "98765-4332" :TYPE "tel")
 (:VALUE "31" :TYPE "tel") (:VALUE "3456-7890" :TYPE "tel")
 (:VALUE "on" :TYPE "checkbox") (:VALUE "on" :TYPE "checkbox")
 (:VALUE "on" :TYPE "checkbox") (:VALUE "on" :TYPE "checkbox")
 (:NAME "login" :TYPE "text") (:NAME "password" :TYPE "password")
 (:NAME "login" :TYPE "text") (:NAME "login" :TYPE "text")
 (:NAME "password" :TYPE "password"))
CL-USER>