一个函数,用于标识一个字符串在lisp中包含在另一个字符串中的次数
我阻止编程lisp函数,该函数标记一个字符串包含在另一个字符串中的次数 我尝试了此函数,它向我发送了一个错误: ***-+:“abc”不是一个数字一个函数,用于标识一个字符串在lisp中包含在另一个字符串中的次数,lisp,common-lisp,Lisp,Common Lisp,我阻止编程lisp函数,该函数标记一个字符串包含在另一个字符串中的次数 我尝试了此函数,它向我发送了一个错误: ***-+:“abc”不是一个数字 (defun string-contain (string1 string2) (cond ((not (length string1)) nil) ; string1 est vide (pas besoin de le tester à chaque fois) ((> (length string1) (length st
(defun string-contain (string1 string2)
(cond
((not (length string1)) nil) ; string1 est vide (pas besoin de le tester à chaque fois)
((> (length string1) (length string2)) nil) ; string1 est plus longue que chaine2
((string= string1 (subseq string2 0 (length string1))) string1)
(t (+ 1(string-include string1 (subseq string2 1))))))
感谢您查看代码,这看起来像是错误的来源:
((string= string1 (subseq string2 0 (length string1))) string1)
此行将返回一个字符串,如果比较成功,它可能会返回“1加上检查string1是否位于“string2的开头,前面一个字符”的值
在默认情况下(不匹配),您可能还希望跳过
(+1…
)。在基本情况下,您肯定希望返回0,而不是nil
。查看代码,这看起来像是错误的来源:
((string= string1 (subseq string2 0 (length string1))) string1)
此行将返回一个字符串,如果比较成功,它可能会返回“1加上检查string1是否位于“string2的开头,前面一个字符”的值
在默认情况下(不匹配),您可能还希望跳过
(+1…
)。在基本情况下,您肯定希望返回0,而不是nil
。(不是(长度字符串))
将始终为false或表示类型错误。您可能希望将0与zerop
(不是(长度字符串))
进行比较,它将始终为false或表示类型错误。您可能希望将0与zerop
进行比较,您的函数有三个肉眼可以注意到的问题:
(不是(长度字符串1))
将始终是nil
,正如斯万特指出的那样nil
,在最后一个分支中返回一个数字。这种不一致性将来可能会导致问题字符串include
- 如果第一个字符串(“子字符串”)比第二个字符串短,则答案必须为0
- 如果第一个字符串的长度等于第二个字符串的长度,这些字符串相等,则答案必须是1
- 如果第一个字符串比第二个字符串短,但从一开始就是它的一部分,我们发现1个包含项,加上,我们需要检查第二个字符串的其余部分(尾部)是否包含相同的子字符串
- 任何其他内容都必须导致0
(defun substring-times (substr string)
(cond ((> (length substr) (length string)) 0)
((and (= (length substr) (length string))
(string= substr string))
1)
((string= substr (subseq string 0 (length substr)))
(1+ (substring-times substr (subseq string (length substr)))))
(t 0)))
我们可以在上面测试
> (substring-times "ab" "abababababc")
5
此函数不包括“cabxabyab”中包含“ab”的情况。但变化是微不足道的(正如他们喜欢在书中所说的,只是一个练习)
更有趣的是,这种函数效率很低(它在迭代的地方使用递归),并且在公共Lisp中不是惯用的。最好使用迭代重写它:
(defun substring-times (substr string)
(let ((sublen (length substr))
(len (length string))
(result 0)
(i 0))
(loop
while (<= i (- len sublen))
if (string= substr string :start2 i :end2 (+ i sublen))
do (progn
(incf result)
(incf i sublen))
else
do (incf i)
end
finally (return result))))
编辑:我已经用Rainer Joswig建议的
string=
关键字替换了subseq
。你的函数有三个肉眼可见的问题:
(不是(长度字符串1))
将始终是nil
,正如斯万特指出的那样nil
,在最后一个分支中返回一个数字。这种不一致性将来可能会导致问题字符串include
- 如果第一个字符串(“子字符串”)比第二个字符串短,则答案必须为0
- 如果第一个字符串的长度等于第二个字符串的长度,这些字符串相等,则答案必须是1
- 如果第一个字符串比第二个字符串短,但从一开始就是它的一部分,我们发现1个包含项,加上,我们需要检查第二个字符串的其余部分(尾部)是否包含相同的子字符串
- 任何其他内容都必须导致0
(defun substring-times (substr string)
(cond ((> (length substr) (length string)) 0)
((and (= (length substr) (length string))
(string= substr string))
1)
((string= substr (subseq string 0 (length substr)))
(1+ (substring-times substr (subseq string (length substr)))))
(t 0)))
我们可以在上面测试
> (substring-times "ab" "abababababc")
5
此函数不包括“cabxabyab”中包含“ab”的情况。但变化是微不足道的(正如他们喜欢在书中所说的,只是一个练习)
更有趣的是,这种函数效率很低(它在迭代的地方使用递归),并且在公共Lisp中不是惯用的。最好使用迭代重写它:
(defun substring-times (substr string)
(let ((sublen (length substr))
(len (length string))
(result 0)
(i 0))
(loop
while (<= i (- len sublen))
if (string= substr string :start2 i :end2 (+ i sublen))
do (progn
(incf result)
(incf i sublen))
else
do (incf i)
end
finally (return result))))
编辑:正如Rainer Joswig所建议的那样,我已经用
字符串=
的关键字替换了subseq
。一般来说,当你处理字符串时,你应该尽量避免调用subseq,因为它会创建一个新字符串,你不想做所有的字符串分配。Common Lisp中的许多序列处理函数都采用开始和结束参数,因此您可以指定要查找的序列的哪些部分。函数search在另一个序列中查找一个序列的匹配项,并返回第一个匹配项的索引。您可以使用新的:start2值重复调用,以便在字符串中搜索得越来越远。例如:
(defun search-all (needle haystack &key key (test 'eql)
(start1 0)
(end1 (length needle))
(start2 0)
(end2 nil)
(overlaps nil))
"Counts the number of times that NEEDLE appears in HAYSTACK. START1
and END1, and START2 and END2, are bounding index designators of
NEEDLE and HAYSTACK, respectively. If OVERLAPS is true, then
overlapping occurrences will be counted separately."
(do* ((len1 (- end1 start1)) ; length of needle (constant)
(upd (if overlaps 1 len1)) ; how much to increment pos
(occurrences 0 (1+ occurrences)) ; occurrences, increments by 1
(start2 start2 (+ pos upd)) ; start2, updated to pos+upd
(pos #1=(search needle haystack ; pos. of needle, or NIL
:start1 start1 :end1 end1
:start2 start2 :end2 end2
:test test :key key)
#1#))
((null pos) occurrences))) ; when pos is NIL, return occurrences
其中有一点可能有点混乱。do和do*循环中的变量绑定具有以下形式(变量[init form[update form]]),我们希望pos的init form和update form相同,即调用search。在通用Lisp代码中,您可以使用#n=form,然后使用#n#稍后再次引用相同的表单。这就是为什么我使用#1=(search…作为初始表单,然后使用#1#作为更新表单
以下是一些例子:
;; Find 'ab' within a 'abcdabcd'
(SEARCH-ALL "ab" "abcdabcd")
;;=> 2
;; Find 'cat' within a 'one cat two cat three cat'
(SEARCH-ALL "concatenate" "one cat two cat three cat" :START1 3 :END1 6)
;;=> 3
;; Find 'cat' within 'one cat two cat'
(SEARCH-ALL "concatenate" "one cat two cat three cat" :START1 3 :END1 6 :START2
0 :END2 15)
;;=> 2
;; Fail to find 'cat' in 'Cat'
(SEARCH-ALL "cat" "Cat")
;;=> 0
;; Find 'cat' in 'Cat'
(SEARCH-ALL "cat" "Cat" :TEST 'CHAR-EQUAL)
;;=> 1
;; Find 2 'aaa' in 'baaaaaab' (no overlaps)
(SEARCH-ALL "aaa" "baaaaaab" :OVERLAPS NIL)
;;=> 2
;; Find 4 'aaa' in 'baaaaaab' (with overlaps)
(SEARCH-ALL "aaa" "baaaaaab" :OVERLAPS T)
;;=> 4