问题描述
我有这个清单:
catch
从语义上讲,字符串前有关键字,例如 ((":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")
我认为有一些方法可以做到这一点。哪种解决方案是一种优雅的解决方法?
谢谢。
解决方法
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)))
,
我将对数据做一些假设:
- 数据始终采用(键值键值...)形式。
- 数据总是格式良好。
让我们一步一步来。
(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>
,
按照@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))