Common Lisp中的字符串交替大小写。

4

我想编写一个函数,在Common Lisp中返回一个交替大小写格式的字符串。例如,输入"stackoverflow"应返回字符串"StAcKoVeRfLoW"。这是我的尝试,但它只返回一列cons对。我是否在正确的轨道上?

(defun mockify (chars)
  (let ((lst (coerce chars 'list)))
    (if (equal lst nil) nil
        (coerce (cons
                 (cons (char-upcase (car lst)) (char-downcase (cadr lst)))
                      (mockify (cddr lst)))
                'string))))

CL-USER> (mockify "meow")
((#\M . #\e) (#\O . #\w))

代码不会产生错误吗? - coredump
如果输入的长度是奇数而不是偶数,我就这么认为。 - jhsandoval
1
更新:是的,问题中的代码导致错误。我曾经试过将返回值从“'string”更改为“'sequence”,这不会导致错误,并忘记在问题中包含它。 - jhsandoval
4个回答

5

使用MAP:我们正在创建一个新字符串,根据交替的布尔变量,在原始字符串中移动并改变大小写。

CL-USER 353 > (let ((string "stackoverflow")
                    (upcase t))
                (map (type-of string)
                     (lambda (element)
                       (prog1 (if upcase
                                  (char-upcase element)
                                (char-downcase element))
                         (setf upcase (not upcase))))
                     string))
"StAcKoVeRfLoW"

4
(defun mockify (chars)
  (let ((lst (coerce chars 'list)))
    (if (equal lst nil)
        ;; return nil
        nil
        ;; return a string (coerce)
        (coerce 
          ;; a list whose elements are cons-cells, but ...
          (cons (cons (char-upcase (car lst))
                      (char-downcase (cadr lst)))
                ;; ... the rest is computed by calling mockify,
                ;; which returns either an empty list or a string
                (mockify (cddr lst)))
          'string))))

你的表达式类型令人困惑,在使用SBCL时会导致错误:

> (mockify "meow")
The value
  (#\O . #\w)
is not of type
  CHARACTER
when setting an element of (ARRAY CHARACTER)
   [Condition of type TYPE-ERROR]

另外,在你的代码中,你需要处理一些特殊情况,因为在现有情况下,可能会调用只有一个元素的列表上的 (cadr list),即 (second list)。这时,结果将为 NIL 并且 char-downcase 将因错误而失败。

仅使用字符串

我建议编写一个不使用中间列表的函数版本:

  1. 令 R 为整个字符串的 string-downcase
  2. 然后修改 R 的每隔一个字符并将其大写

例如,实现它的一种方式 (还有其他的):

(defun mockify (chars)
  (let ((chars (string-downcase chars)))
    (prog1 chars
      (upcasify chars 0))))

(defun upcasify (string index)
  (when (< index (length string))
    (setf (char string index) (char-upcase (char string index)))
    (upcasify string (+ index 2))))

仅使用列表

如果您更喜欢使用递归函数来处理列表,我会更愿意分层定义它:

  1. 将字符串转换为列表
  2. 递归地处理列表
  3. 最后将结果列表转回字符串

这样可以避免在每个步骤中进行字符串到列表的转换,并使代码在每个级别上更简单。

(defun mockify (chars)
  (coerce (mockify-list (coerce chars 'list)) 'string))

(defun mockify-list (chars)
  ...)

列表版本是递归的,看起来与您尝试的操作类似,但要注意边角情况。


1
谢谢你帮我以不同的方式思考这个问题。这正是我需要的——首先将整个字符串转换为小写,然后将每个其他字符转换为大写。 - jhsandoval

4

有不止一种方法来实现它。这里是一种基于 loop 的解决方案:

(let ((string "StackOverflow"))
  (with-output-to-string (s)
    (loop :for c :across string
          :for up := t :then (not up)
          :do (princ (if up
                         (char-upcase c)
                         (char-downcase c))
                     s))))

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接