在 Common Lisp 中为字符串交替 upcase/downcase

Alternating upcase/downcase for a string in Common Lisp

我想编写一个函数,它将 return 一个用 Common Lisp 中的替代 upcase/downcase 格式化的字符串。例如,输入“Whosebug”应该 return 字符串“Whosebug”。这是我的尝试,但它只是 return 一个缺点对列表。我在正确的轨道上吗?

(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))
(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)
  ...)

列表版本是递归的,看起来像您尝试做的,但要注意极端情况。

使用 MAP:我们正在创建一个新字符串,根据交替布尔变量移动原始字符串和 upcase/downcase。

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

有不止一种方法可以做到。这是一个基于 loop 的解决方案:

(let ((string "Whosebug"))
  (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))))

有趣的东西 - 我前段时间写过类似的东西。

https://github.com/phoe/string-pokemonize