String 不使用Common LISP中的REVERSE函数反转字符串
我正在创建一个程序,可以确定最终项目中的字符串是否为回文。但是,我不允许使用String 不使用Common LISP中的REVERSE函数反转字符串,string,lisp,common-lisp,palindrome,String,Lisp,Common Lisp,Palindrome,我正在创建一个程序,可以确定最终项目中的字符串是否为回文。但是,我不允许使用REVERSE功能 (defun palindrome(x) (if (string= x (reverse x)) (format t "~d" ": palindrome" (format t x)) (format t "~d" ": not palindrome" (format t x)))
REVERSE
功能
(defun palindrome(x)
(if (string= x (reverse x))
(format t "~d" ": palindrome" (format t x))
(format t "~d" ": not palindrome" (format t x)))
)
这是我迄今为止对反向函数的尝试
(defun palindrome(x)
(if (string= x (reverse x))
(format t "~d" ": palindrome" (format t x))
(format t "~d" ": not palindrome" (format t x)))
)
我想出的解决这个问题的方法如下。这可能非常复杂
-将字符串拆分为单独的字符,并将它们连接到一个变量中(最后一个字符排在第一位),然后与输入进行比较
-通过递归将每个字符写入另一个变量,从最后一个字符开始,然后与输入进行比较
我只是不知道如何将上述任何一种方法实现到lisp中。反转字符串
我非常确定在练习中不使用反向
的限制是为了避免像(string=s(反向s))
这样的琐碎回答(注意,这种方法做的工作太多,因为您只需要检查一半字符是否相等)和避免生成中间字符串。因此,我猜您可能不会使用copy seq
或make string
,或任何其他分配字符串的标准函数。因此,即使有一种方法可以写一个定制的反面,在你的约束条件下,它也能满足你的需求,这可能会违背练习的精神
例如,这里有一个my reverse
函数,它依赖于和。同样,我不认为你应该用它来解决这个问题,这只是为了说明如何做到:
(defun my-reverse (s)
(do* ((n (length s))
(z (make-string n))
(i 0 (1+ i))
(j (1- n) (1- j)))
((>= i n) z)
(setf (char z i) (char s j))))
基本上,i
从零开始增加,j
从字符串中的最后一个位置开始减少,循环的每次迭代都将结果字符串中i
位置的字符设置为原始字符串中j
位置的字符
回文检查
您可以通过查看字符串来编写回文检查,而无需创建新字符串并将其反转。例如,您可以调整上面的循环,以便它检查回文,而不是创建新字符串
另一种可能的实现方法是遵循回文的递归定义:
0: (PALINDROMEP "ABCDEF")
1: (BOUNDED-PALINDROME-P "ABCDEF" 0 5)
1: BOUNDED-PALINDROME-P returned NIL
0: PALINDROMEP returned NIL
- 空字符串是回文
- 一个字符的字符串是回文
- 如果S是回文,C是字符,那么CSC是回文
0: (PALINDROMEP "")
1: (BOUNDED-PALINDROME-P "" 0 -1)
1: BOUNDED-PALINDROME-P returned T
0: PALINDROMEP returned T
单字母字符串:
0: (PALINDROMEP "A")
1: (BOUNDED-PALINDROME-P "A" 0 0)
1: BOUNDED-PALINDROME-P returned T
0: PALINDROMEP returned T
长度超过1的回文:
0: (PALINDROMEP "ABCBA")
1: (BOUNDED-PALINDROME-P "ABCBA" 0 4)
2: (BOUNDED-PALINDROME-P "ABCBA" 1 3)
3: (BOUNDED-PALINDROME-P "ABCBA" 2 2)
3: BOUNDED-PALINDROME-P returned T
2: BOUNDED-PALINDROME-P returned T
1: BOUNDED-PALINDROME-P returned T
0: PALINDROMEP returned T
非回文:
0: (PALINDROMEP "ABCDEF")
1: (BOUNDED-PALINDROME-P "ABCDEF" 0 5)
1: BOUNDED-PALINDROME-P returned NIL
0: PALINDROMEP returned NIL
另一个有一些共同字母的失败测试:
0: (PALINDROMEP "ABCDEFBA")
1: (BOUNDED-PALINDROME-P "ABCDEFBA" 0 7)
2: (BOUNDED-PALINDROME-P "ABCDEFBA" 1 6)
3: (BOUNDED-PALINDROME-P "ABCDEFBA" 2 5)
3: BOUNDED-PALINDROME-P returned NIL
2: BOUNDED-PALINDROME-P returned NIL
1: BOUNDED-PALINDROME-P returned NIL
0: PALINDROMEP returned NIL
我想说,最简单的方法是简单的循环,如下所示:
(defun palindrome? (s)
(not
(loop for c1 across s
for end from (1- (length s)) downto (/ (length s) 2)
when (char/= c1 (char s end))
do (return t))))
如果在每个步骤上缩小边界,则从结尾开始,检查字符是否相等,并在遇到不相等的字符时立即停止(迭代部分确保循环在到达字符串中间时结束,这意味着它是回文)
更新
正如@coredump所建议的,这一点可以通过以下方式简化:
(defun palindrome? (s)
(loop for c1 across s
for end from (1- (length s)) downto (/ (length s) 2)
always (char= c1 (char s end))))
通过递归
查找第一个和最后一个元素
字符串的字符列表,在第一次不合格之前它们是否相等
发生并返回nil
。
测试的第一个和最后一个元素从列表中切掉
通过cdr
和butlast
;; helper function working with list of chars
(defun %palindromep (chars)
(cond ((null chars) t)
((char= (first chars) (car (last chars)))
(%palindromep (butlast (cdr chars))))
(t nil)))
;; function working with strings
(defun palindomep (s)
(%palindromep (coerce s 'list)))
这个答案特别关注列表(不是向量)的递归回文检查,作为对用户答案的评论的后续。有一种递归方法可以检查回文列表,它不分配新列表,只在O(n)时间内迭代给定列表,即在线性时间内,而不是二次时间内 为了解释这种方法,让我定义一个辅助函数,
映射对立
,它是一个高级函数,将函数映射到列表两端的值。例如,(映射相反的f'(abc))
对以下参数对调用f
:(caa)
,(bb)
和(acc)
(按该顺序)。然后,palindrome
check函数将只是映射对立面的一个应用程序,带有一个闭包(提前退出)
为了使映射对立面
对回文检查有用(可能还有其他功能),它还跟踪列表中每个元素的当前索引。
映射对向
调用的函数应接受两个值x
和y
,以及两个索引x-index
和y-index
映射对立
让我们定义一个辅助函数并跟踪它,以及映射对立面%
:
(defun dbg (&rest args)
(declare (ignore args)))
(trace map-opposites% dbg)
使用此输入调用主函数:
(map-opposites 'dbg '(a b c d e))
提供以下跟踪(为清晰起见,已编辑包前缀):
主要有两个区域:
首先,函数递归到最终的空列表,同时计算列表的大小;请注意,整个原始列表作为第三个参数未经修改地传递
当展开堆栈时,整个列表将被第二次遍历(请参见第二次返回值),而调用堆栈将向上移动。事实上,有人可能会认为调用堆栈扮演着列表副本的角色
更准确地说,请注意:
在递归的底部,函数返回列表的大小以及整个列表。当堆栈向上展开时,整个列表将向下访问
在递归的中间级别,我们有当前值h1
(向下递归时获得的值),第二个值h2
通过迭代整个列表(c)获得
0: (MAP-OPPOSITES% DBG (A B C D E) (A B C D E) 0)
1: (MAP-OPPOSITES% DBG (B C D E) (A B C D E) 1)
2: (MAP-OPPOSITES% DBG (C D E) (A B C D E) 2)
3: (MAP-OPPOSITES% DBG (D E) (A B C D E) 3)
4: (MAP-OPPOSITES% DBG (E) (A B C D E) 4)
5: (MAP-OPPOSITES% DBG NIL (A B C D E) 5)
5: MAP-OPPOSITES% returned 0 (A B C D E)
5: (DBG E A 4 0)
5: DBG returned NIL
4: MAP-OPPOSITES% returned 1 (B C D E)
4: (DBG D B 3 1)
4: DBG returned NIL
3: MAP-OPPOSITES% returned 2 (C D E)
3: (DBG C C 2 2)
3: DBG returned NIL
2: MAP-OPPOSITES% returned 3 (D E)
2: (DBG B D 1 3)
2: DBG returned NIL
1: MAP-OPPOSITES% returned 4 (E)
1: (DBG A E 0 4)
1: DBG returned NIL
0: MAP-OPPOSITES% returned 5 NIL
(defun palindrome (list)
(prog1 t
(map-opposites (lambda (x y ix iy)
(when (<= ix iy)
(return-from palindrome t))
(unless (eql x y)
(return-from palindrome nil)))
list)))
(defun map-opposite-indices% (fun list i1)
(if list
(let ((i2 (map-opposite-indices% fun (rest list) (1+ i1))))
(funcall fun i1 i2)
(1+ i2))
0))
(defun map-opposite-indices (fun list)
(map-opposite-indices% fun list 0))
(defun dbg (&rest args)
(print args))
(map-opposite-indices 'dbg '(a b c d e))
(4 0)
(3 1)
(2 2)
(1 3)
(0 4)
(defun map-opposites% (fun l1 whole)
(if l1
(destructuring-bind (h1 . t1) l1
(let ((l2 (map-opposites% fun t1 whole)))
(when l2
(destructuring-bind (h2 . t2) l2
;; in the base case l2 is the whole list (a b c d e)
;; then one level up in the recursion l2 is bound to
;; (b c d e); one level up it is (c d e),
;; then (d e), etc.
;; At the same levels of recursion, l1 is bound
;; respectively to (), then (e), then (d e),
;; then (c d e), etc.
(prog1 t2
(funcall fun h1 h2))))))
whole))
(defun map-opposites (fun list)
(map-opposites% fun list list))
(defun dbg (&rest args)
(print args))
(map-opposites 'dbg '(a b c d e))
(E A)
(D B)
(C C)
(B D)
(A E)