2

我正在尝试编写一个函数,通过以下方式将关键字参数传递给函数

 (defun hyphenate (string &key upper lower)
  (do ((s (cdr (coerce string 'list)) (cdr s))
       (acc (string (char string 0))))
      ((null s) (cond
                 (lower (string-downcase acc))
                 (t (string-upcase acc))))
     (cond
      ((upper-case-p (car s)) (setf acc (concatenate 'string
                                                     (concatenate 'string acc "-")
                                                     (string (car s)))))
      (t (setf acc (concatenate 'string acc (string (car s))))))))) 

基本上,如果函数接收到关键字upper,它会调用string-upcase,如果它接收到关键字lower,它会做一个string-downcase。

我只是不知道在我的函数中测试这些参数的适当方法是什么。我不想将它们绑定到一个值。我只想这样称呼他们

(连字符“jobPostings”:上)

如何检查函数调用中是否存在 :upper?它一直告诉我有一个“传递给连字符的未配对关键字”

4

3 回答 3

6

作为参数的关键字和关键字参数是两个不同的东西。关键字参数是命名参数。它们有两个项目名称

像这样:

CL-USER 1 > (defun hyphenate (string &key upper lower) (list string upper lower))
HYPHENATE

您需要提供名称和值。请注意,未传入的关键字参数的值为 NIL。

CL-USER 2 > (hyphenate "foo" :upper t)
("foo" T NIL)

将其与可选参数进行比较:

CL-USER 3 > (defun hyphenate (string &optional case) (list string case))
HYPHENATE

现在你只需要给出可选参数,它可以是符号:upper

CL-USER 4 > (hyphenate "foo" :upper)
("foo" :UPPER)

或者您使用命名关键字参数,在其中传入大小写符号:

CL-USER 5 > (defun hyphenate (string &key case) (list string case))
HYPHENATE

同样,作为两项:名称和值:

CL-USER 6 > (hyphenate "foo" :case :upper)
("foo" :UPPER)

关于您的功能的一些评论:

  • 如果您检查谓词,请使用 IF,而不是 COND

  • 您正在迭代字符串并首先将其转换为字符串。通常你会使用索引来遍历字符串。

  • DO您在循环中一遍又一遍地将单个字符连接到一个字符串。太丑了 如果您已经在使用列表作为输入,为什么不在输出也使用列表并在退出时将其转换回字符串?

如果您想坚持使用列表的想法,您需要在列表上进行映射。

(defun hyphenate (string  &key (case :upper))
  (map 'string
       (if (eq case :upper) #'char-upcase #'char-downcase)
       (destructuring-bind (start . rest)
           (coerce string 'list)
         (cons start
               (mapcan (lambda (char)
                         (if (upper-case-p char)
                             (list #\- char)
                           (list char)))
                       rest)))))

MAPCAN插入必要的连字符。外部MAP转换大小写并返回一个字符串。

于 2012-12-03T07:36:20.987 回答
2

这不是关键字参数的工作方式。

鉴于这种:

(defun hyphenate (string &key upper lower)
  ...)

你会像这样调用它:

> (hyphenate "fooBar" :lower t)
"foo-bar"
> (hyphenate "fooBar" :upper t)
"FOO-BAR"
> (hyphenate "fooBar")
"FOO-BAR"
> (hyphenate "fooBar" :upper t :lower t)
"foo-bar"
> (hyphenate "fooBar" :upper)
ERROR: keyword argument list not of even length
[1]> 

基本上,关键字参数在必需和可选参数之后作为内联属性列表给出。

也许你想要的是:

(defun hyphenate (string &optional (case :lower))
  (assert (member case '(:lower :upper)))
  (let ((lower (eq case :lower)))
    ...))

现在你这样称呼它:

> (hyphenate "fooBar" :lower)
"foo-bar"
> (hyphenate "fooBar" :upper)
"FOO-BAR"
> (hyphenate "fooBar")
"foo-bar"
> (hyphenate "fooBar" nil)
ERROR: (assert (member nil '(:lower :upper))) failed
[1]> 

您可能需要重新考虑要接受的函数,nil以便不调用任何 case 函数。但由于我不知道你将如何使用它,这只是一个建议。

但是,您还应该为此功能考虑其他事项。

例如,您正在将原始字符串转换为列表。如果hyphenate经常调用,您可能会注意到性能下降。如果您直接访问原始字符串会更好。

您还可以使用(make-string (+ (length string) num-hyphens)).

最后,您可以使用nstring-upcaseandnstring-downcase,因为生成的字符串总是新鲜的。


PS:在 Common Lisp 中,可以知道在lambda 列表中的参数声明中是否实际上为可选或关键字参数提供了额外的变量:

(defun foo (string &optional (opt (default-opt-expression) opt-supplied-p))
  ...)

(defun bar (string &key (key (default-key-expression) key-supplied-p))
  ...)

在这些示例中,opt-supplied-pandkey-supplied-p是布尔值,说明是否提供了参数。

于 2012-12-04T11:15:51.200 回答
1

如果我理解正确,你想要这样的东西,对吧?

(ql:quickload "cl-ppcre")
(defun hyphenate (string &key (transform-case #'identity))
  (reduce
   #'(lambda (a b)
       (concatenate 'string a (when (> (length a) 0) "-")
                    (funcall transform-case b)))
          (cl-ppcre:split "(?=[A-Z])" string) :initial-value ""))

(hyphenate "fooBarBaz")
"foo-Bar-Baz"

(hyphenate "fooBarBaz" :transform-case #'string-downcase)
"foo-bar-baz"

这也减少了consing / coercion,您可以编写更多的转换函数来处理字符,例如音译它们或您喜欢的任何东西。

如果你的关键参数不是一个函数,你可以做一些事情来达到这个效果:

(ccase key-argument
  (possilbe-value-0 (do what possible value 0 does))
  (possilbe-value-1 (do what possible value 1 does))
  . . .
  (possilbe-value-N (do what possible value N does)))

例如,但确实有很多方法可以做到这一点。


类似,但没有 ppcre:

(defun hyphenate (string &key (case-transform #'identity))
  (with-output-to-string (stream)
    (loop for c across string
       do (if (upper-case-p c)
              (progn
                (when (> (file-position stream) 0)
                  (write-char #\- stream))
                (write-char (funcall case-transform c) stream))
              (write-char c stream)))))

(hyphenate "fooBarBaz")
"foo-Bar-Baz"

(hyphenate "fooBarBaz" :case-transform #'char-downcase)
"foo-bar-baz"
于 2012-12-03T06:09:22.153 回答