我下面有一个元素列表
("(aviyon" "213" "flyingman" "no))") as list
我想要的是我想使用括号作为分隔符拆分包含字符串的此列表,而且还希望在不破坏顺序的情况下将这些括号包含在新列表中
我想要的新列表输出(或修改的相同列表)
("(" "aviyon" "213" "flyingman" "no" ")" ")")
我来自命令式语言,这在Java或C ++中将是15分钟的工作。但是在这里我被困在做什么。我知道我必须
1-循环从列表中获取元素
我认为这是通过(nth 1 '(listname) )
2-分开而不删除放入新列表的定界符
我发现了诸如SPLIT-SEQUENCE之类的功能,但我不能不删除它,也不会破坏原始顺序。
任何帮助将不胜感激。
答案 0 :(得分:4)
您可以使用cl-ppcre库来完成这项工作。
例如:
CL-USER> (ql:quickload :cl-ppcre)
CL-USER> (cl-ppcre:split "([\\(\\)])" "(aviyon" :with-registers-p t)
("" "(" "aviyon")
CL-USER> (cl-ppcre:split "([\\(\\)])" "no))" :with-registers-p t)
("no" ")" "" ")")
CL-USER>
但是,它会在列表中产生空字符串。使用remove-if
函数摆脱它们:
CL-USER> (defun empty-string-p (s) (string= s ""))
EMPTY-STRING-P
CL-USER> (remove-if 'empty-string-p
(list "no" ")" "" ")"))
("no" ")" ")")
最后,您可以构造一个实现上述两个功能的函数,然后在imperative
循环中运行它(是的,Common Lisp的功能并不像很多人想象的那样):
CL-USER> (defun remove-empty-strings (l)
(remove-if 'empty-string-p l))
REMOVE-EMPTY-STRINGS
CL-USER> (defun split (s)
(cl-ppcre:split "([\\(\\)])"
s
:with-registers-p t))
SPLIT
CL-USER> (defparameter *the-list* '("(aviyon" "213" "flyingman" "no))"))
*THE-LIST*
CL-USER> (loop for item in *the-list*
for splitted = (split item)
for cleaned = (remove-empty-strings splitted)
append cleaned)
("(" "aviyon" "213" "flyingman" "no" ")" ")")
答案 1 :(得分:2)
让我们有另一个答案,而无需外部库。 就像您已经做过的一样,我们可以将问题分解为较小的部分:
all-tokens
将此函数应用于输入列表中的所有字符串,并连接结果:
(mapcan #'all-tokens strings)
第一部分获取状态并从中建立列表,看起来像unfold
操作(变形)。
在Lisp中称为reduce
的折叠(变形)从值列表和函数(以及可选的初始值)构建值。
对偶运算unfold
获取一个值(状态),一个函数并生成值列表。
对于unfold
,step函数接受一个状态并返回新状态以及结果列表。
在这里,让我们将状态定义为3个值:一个字符串,该字符串的起始位置以及到目前为止已解析的标记堆栈。
我们的步进函数next-token
返回下一个状态。
;; definition follows below
(declare (ftype function next-token))
从字符串中获取所有标记的主要功能只是计算一个固定点:
(defun all-tokens (string)
(do (;; initial start value is 0
(start 0)
;; initial token stack is nil
(tokens))
;; loop until start is nil, then return the reverse of tokens
((not start) (nreverse tokens))
;; advance state
(multiple-value-setq (string start tokens)
(next-token string start tokens))))
我们需要一个辅助功能:
(defun parenthesisp (c)
(find c "()"))
step函数定义如下:
(defun next-token (string start token-stack)
(let ((search (position-if #'parenthesisp string :start start)))
(typecase search
(number
;; token from start to parenthesis
(when (> search start)
(push (subseq string start search) token-stack))
;; parenthesis
(push (subseq string search (1+ search)) token-stack)
;; next state
(values string (1+ search) token-stack))
(null
;; token from start to end of string
(when (< start (1- (length string)))
(push (subseq string start) token-stack))
;; next-state
(values string nil token-stack)))))
您可以尝试使用单个字符串:
(next-token "(aviyon" 0 nil)
"(aviyon"
1
("(")
如果获取结果状态值并重用它们,那么您将:
(next-token "(aviyon" 1 '("("))
"(aviyon"
NIL
("aviyon" "(")
在这里,第二个返回值是NIL,它结束了生成过程。 最后,您可以执行以下操作:
(mapcan #'all-tokens '("(aviyon" "213" "flyingman" "no))"))
哪个给:
("(" "aviyon" "213" "flyingman" "no" ")" ")")
从all-tokens
对next-token
知道太多的角度来看,上述代码不是完全通用的:您可以将其重写为任何状态。
您还可以通过将更多信息保留在状态变量中,使用相同的机制来处理字符串序列。
另外,在真正的词法分析器中,您不希望反转整个标记列表,而是使用队列来提供解析器。
答案 2 :(得分:0)
解决方案
由于您不了解Alexander的解决方案,而且由于我还是写了我的解决方案,所以:
;; load two essential libraries for any common lisper
(ql:quickload :cl-ppcre)
(ql:quickload :alexandria)
;; see below to see how to install quicklisp for `ql:quickload` command
;; it is kind of pythons `import` and if not install `pip install`
;; in one command for common-lisp
(defun remove-empty-string (string-list)
(remove-if #'(lambda (x) (string= x "")) string-list))
(defun split-parantheses-and-preserve-them (strings-list)
(remove-empty-string
(alexandria:flatten
(mapcar #'(lambda (el) (cl-ppcre:split "(\\(|\\))"
el
:with-registers-p t))
strings-list))))
;; so now your example
(defparameter *list* '("(aviyon" "213" "flyingman" "no))"))
(split-parantheses-and-preserve-them *list*)
;; returns:
;; ("(" "aviyon" "213" "flyingman" "no" ")" ")")
这是如何工作的
(cl-ppcre:split "(\\(|\\))" a-string)
用(
或)
分割字符串。因为在正则表达式模式中,(
或)
用于捕获匹配项-就像这里一样(外部括号捕获)-您必须转义它们。 \\(
或\\)
。
因此,使用cl-ppcre:split
可以通过regex-pattern分割通用lisp中的任何字符串。 Edi Weitz编写的超酷,超高效的软件包。他为常见的Lisp写了几个非常复杂的软件包-在社区中也被称为ediware或edicls。
顺便说一下,对于正则表达式,cl-ppcre甚至比黄金标准的效率更高,速度更快:Perl正则表达式引擎!
:with-regiesters-p t
选项将保留匹配的定界符-必须用如下括号括起来:(<pattern>)
在模式中。
mapcar
遍历列表,将其应用于字符串列表中的每个字符串元素。
但是,之后得到的是列表列表。 (每个内部列表都包含列表中每个字符串元素的拆分结果)。
用alexandria:flatten
来平铺列表。
对于不是Lisp标准的许多功能,但是您认为它们是基本的功能(如平化列表),总是在亚历山大中始终优先出现-大多数功能都具有您想要的功能-这是一个庞大的库。这就是为什么无论如何您都需要它作为普通的lisper;)。
但是,仍然会有空字符串要删除。
这就是为什么我写了remove-empty-string
并使用remove-if
的原因-与remove-if-not
一起是列表的标准过滤功能。
它具有谓词功能-这里的(lambda (x) (string= x ""))
如果string为空字符串则为T,否则为NIL。
它将删除函数中结果平化列表中的所有元素,这些元素为空字符串。
在其他语言中,它将被命名为filter
,但是是的-有时common-lisp中的函数名称不是很好选择。有时,我认为我们应该创建别名,然后再移至它们,并保留旧名称以实现向后兼容。 Clojure具有更好的函数名称...也许人们应该取代Clojure函数名称...
快速
@Alexander Artemenko确切地写了我的解决方案-他是第一位。我会加: 如果您对通用Lisp不太熟悉,也许您不知道如何使用quicklisp。 在终端机(Linux或MacOS)中执行:
wget https://beta.quicklisp.org/quicklisp.lisp
否则,请从该地址在Windows中手动下载。
我将其放入~/quicklisp
文件夹中。
然后在clisp或sbcl中执行:
(load "~/quicklisp/quicklisp.lisp") ;; just path to where downloaded
;; quicklisp.lisp file is!
;; then install quicklisp:
(quicklisp-quickstart:install)
;; then search for cl-ppcre
(ql:system-apropos "cl-ppcre")
;; then install cl-ppcre
(ql:quickload "cl-ppcre")
;; and to autoload everytime you start sbcl or clisp
;; in linux or mac - sorry I don't now windows that well
;; I have the opinion every programmer should us unix
;; as their OS
;; you have to let quicklisp be loaded when they start
;; by an entry into the init file
;; mostly located in ~/.sbclrc or ~/.clisprc.slip or such ...
;; respectively.
;; quicklisp does an entry automatically if you do:
(ql:add-to-init-file)
;; after installation do:
(quit)
;; If you then restart sbcl or clisp and try:
(ql:quickload :cl-ppcre)
;; it should work, - if not, you have to manually load
;; quicklisp first
(load "~/quicklisp/setup.lisp") ;; or wherever quicklisp's
;; setup.lisp file has been stored in your system!
;; and then you can do
(ql:quickload :cl-ppcre)
;; to install alexandria package then, do
(ql:quickload :alexandria) ;; or "alexandria"
;; ql:quickload installs the package from quicklisp repository,
;; if it cannot find package on your system.
;; learn more about quicklisp, since this is the package
;; manager of common lisp - like pip for python