检查lisp中是否存在关键字参数

时间:2012-12-03 04:03:16

标签: lisp common-lisp

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

 (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,如果它接收到更低的键,它将执行字符串缩减。

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

(连字符“jobPostings”:upper)

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

3 个答案:

答案 0 :(得分: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转换大小写并返回一个字符串。

答案 1 :(得分: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]> 

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

也许你想要的是:

(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,以便不会调用大小写函数。但由于我不知道你将如何使用它,这只是一个建议。

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

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

您还可以使用(make-string (+ (length string) num-hyphens))预先分配新字符串。

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


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

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

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

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

答案 2 :(得分: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"

这也不那么消耗/强制,你可以写更多的转换函数来处理字符,比如音译或者你喜欢的任何东西。

如果你的关键参数不是函数,你可以做一些这样的事情:

(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"