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

检查lisp中是否存在关键字参数,lisp,common-lisp,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)) (

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

 (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,它将调用字符串upcase,如果它接收到关键字lower,它将执行字符串downcase

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

(连字符“jobPostings”:上方)


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

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

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

关键字作为参数和关键字参数是两件不同的事情。关键字参数是命名参数。它们以两项形式出现:一个名称和一个值

像这样:

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)
或者使用命名关键字参数,在其中传递case符号:

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
插入必要的连字符。外部
映射
转换大小写并返回字符串。

关键字参数不是这样工作的

鉴于此:

(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]> 
> (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]> 
基本上,关键字参数在必需参数和可选参数之后作为内联参数提供

也许你想要的是:

(defun hyphenate (string &optional (case :lower))
  (assert (member case '(:lower :upper)))
  (let ((lower (eq case :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]> 
> (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))
预先分配新字符串

最后,您可以使用,因为生成的字符串总是新的


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


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

为什么不绑定我的关键字参数?!?!