String 拆分字符串,即使最后一个字符是分隔符

String 拆分字符串,即使最后一个字符是分隔符,string,split,lisp,common-lisp,String,Split,Lisp,Common Lisp,我想删除字符串末尾的一些字符 我做了这个功能: (defun del-delimiter-at-end (string) (cond ((eq (delimiterp (char string (- (length string) 1))) nil) string ) (t (del-delimiterp-at-end (subseq string 0 (- (length string) 1))) ) ) ) 为此: (defun de

我想删除字符串末尾的一些字符

我做了这个功能:

(defun del-delimiter-at-end (string)
  (cond
    ((eq (delimiterp (char string (- (length string) 1))) nil) 
        string )
    (t 
        (del-delimiterp-at-end (subseq string 0 (- (length string) 1))) ) ) )
为此:

(defun delimiterp (c) (position c " ,.;!?/"))
但我不明白为什么它不起作用。我有以下错误:

Index must be positive and not -1
请注意,我想在字符串列表中拆分一个字符串,我已经在这里查看了:

但是如果字符串的结尾是分隔符,那么它就不起作用,这就是为什么我要这么做

我做错了什么? 提前谢谢。

简单的方法 只需使用:

你的错误 如果在末尾向del分隔符传递一个空字符串,则将-1作为第二个参数传递给

你的代码 没有理由这么做。。。无只要使用分隔符。。。取而代之的是转换条款

当您只有两个子句且每个子句只有一种形式时,使用它是惯用的

递归调用,这意味着您不仅无缘无故地分配内存,而且算法的字符串长度也是二次的


这里有两个问题。一个更为具体,并在问题主体中进行了描述。另一个更一般,这是标题中关于如何分割序列的问题。我将处理正文中的直接问题,即如何从序列的末尾修剪一些元素。然后我将处理一个更一般的问题,即如何在一般情况下拆分序列,以及在特殊情况下如何拆分列表,因为根据标题查找此问题的人可能对此感兴趣

右修剪序列 如果你只关心字符串,这是完美的。该语言已经包含字符串右修剪,因此如果您只关心字符串,那么这可能是解决此问题的最佳方法

序列的一种解法 也就是说,如果您希望使用基于subseq的方法来处理任意序列,那么使用该语言提供的其他序列操作函数是有意义的。许多函数采用:from-end参数,并具有-如果不是的话-变体,这会有所帮助。在这种情况下,可以使用position if查找序列中最右边的非分隔符,然后使用subseq:

补语和位置的使用 有些人可能会指出,如果不是这样的话,这种立场是不可取的。如果你不想使用它,你可以使用补语和位置If来达到同样的效果。我还没有注意到对-if-not函数的实际厌恶。补码上的HyperSpec条目表示:

在Common Lisp中,名称为xxx(如果不是)的函数是相关的 如果使用类似xxx的名称,则调用函数

比如说,

 (find-if-not #'zerop '(0 0 3)) == 
 (find-if (complement #'zerop) '(0 0 3)) =>  3
请注意,由于xxx if not功能和:test not 参数已被弃用,使用xxx if函数或:test 带补码的参数是首选参数

也就是说,position和position if not接受函数指示符,这意味着您可以将符号delimiterRP传递给它们,就像我们在中所做的那样

(right-trim-if "hello!" 'delimiterp)    ; some delimiters to trim
;=> "hello"
不过,补码并不需要函数指示符,即符号或函数,它实际上需要一个函数对象。因此,您可以定义右修剪,如果

但您必须使用函数对象而不是符号调用它:

(right-trim-if "hello!" #'delimiterp)
;=> "hello"

(right-trim-if "hello!" 'delimiterp)
; Error
拆分序列 如果您不只是尝试对序列进行正确的修剪,那么您可以实现拆分函数,而不会遇到太多麻烦。其思想是在序列中增加一个开始指针。它首先指向序列的开头。然后找到第一个分隔符并获取它们之间的子序列。然后找到之后的下一个非分隔符,并将其作为新的起点

(defun split (sequence test)
  (do ((start 0) 
       (results '()))
      ((null start) (nreverse results))
    (let ((p (position-if test sequence :start start)))
      (push (subseq sequence start p) results)
      (setf start (if (null p) 
                      nil
                      (position-if-not test sequence :start p))))))
这适用于多种类型的序列,并且不会在子序列中使用非分隔符:

CL-USER> (split '(1 2 4 5 7) 'evenp)
((1) (5 7))
CL-USER> (split '(1 2 4 5 7) 'oddp)
(NIL (2 4))
CL-USER> (split "abc123def456" 'alpha-char-p)
("" "123" "456")
CL-USER> (split #(1 2 3 foo 4 5 6 let 7 8 list) 'symbolp)
(#(1 2 3) #(4 5 6) #(7 8))
虽然这适用于所有类型的序列,但对于列表来说不是很有效,因为subseq、position等都必须遍历列表直到开始位置。对于列表,最好使用特定于列表的实现:

(defun split-list (list test)
  (do ((results '()))
      ((endp list)
       (nreverse results))
    (let* ((tail (member-if test list))
           (head (ldiff list tail)))
      (push head results)
      (setf list (member-if-not test tail)))))

除了if和ldiff会员,您还可以将我们从切至。

再次感谢您的回复和建议!我只想指出,这个错误是因为我没有用新值设置字符串,我忘记了字符串末尾的setq string del delimiter根据您接受的答案,听起来更像是您想知道如何从右侧修剪字符串,但是,即使最后一个字符是分隔符,原始问题标题也会拆分为字符串。你能澄清一下你到底在找什么吗?
(defun right-trim-if (sequence test)
  (let ((pos (position-if (complement test) sequence :from-end t)))
    (subseq sequence 0 (if (null pos) 0 (1+ pos)))))
(right-trim-if "hello!" #'delimiterp)
;=> "hello"

(right-trim-if "hello!" 'delimiterp)
; Error
(defun split (sequence test)
  (do ((start 0) 
       (results '()))
      ((null start) (nreverse results))
    (let ((p (position-if test sequence :start start)))
      (push (subseq sequence start p) results)
      (setf start (if (null p) 
                      nil
                      (position-if-not test sequence :start p))))))
CL-USER> (split '(1 2 4 5 7) 'evenp)
((1) (5 7))
CL-USER> (split '(1 2 4 5 7) 'oddp)
(NIL (2 4))
CL-USER> (split "abc123def456" 'alpha-char-p)
("" "123" "456")
CL-USER> (split #(1 2 3 foo 4 5 6 let 7 8 list) 'symbolp)
(#(1 2 3) #(4 5 6) #(7 8))
(defun split-list (list test)
  (do ((results '()))
      ((endp list)
       (nreverse results))
    (let* ((tail (member-if test list))
           (head (ldiff list tail)))
      (push head results)
      (setf list (member-if-not test tail)))))
CL-USER> (split-list '(1 2 4 5 7) 'oddp)
(NIL (2 4))
CL-USER> (split-list '(1 2 4 5 7) 'evenp)
((1) (5 7))