如何在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")

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

谢谢。

我将对数据做一些假设:

  • 数据总是以(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,你可以使用loop:

(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> 
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)))

根据@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))))))

返回:

((: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))

最新更新