Lisp性能在字符串处理中的最佳解决方案

时间:2013-08-03 15:54:40

标签: string performance lisp common-lisp

我有一个字符串,下划线分隔单词(例如 aaa_bbb_ccc

我创建了一个将字符串转换为camelCase的函数(例如 aaaBbbCcc )。

我想知道是否有一些我做错了会影响性能的事情。这是代码:

(defun underscore-to-camel (input)
        (defparameter input-clean-capitalized (remove #\_ (string-capitalize input)))
        (setf (aref input-clean-capitalized 0) (aref (string-downcase (aref input-clean-capitalized 0)) 0))
        input-clean-capitalized)

我还创建了第二个变体但速度慢了约25%(使用time测量了300万次执行):

(defun underscore-to-camel-v2 (input)
        (defparameter input-clean-capitalized (remove #\_ (string-capitalize input)))
        (concatenate 
            'string 
            (string-downcase (aref input-clean-capitalized 0)) 
            (subseq input-clean-capitalized 1)))

4 个答案:

答案 0 :(得分:3)

首先,defparameter不是你想要使用的。你应该真的重构 你的代码是这样的:

(defun underscore-to-camel (input)
  (let ((input-clean-capitalized (remove #\_ (string-capitalize input))))
    (setf (aref input-clean-capitalized 0)
          (aref (string-downcase (aref input-clean-capitalized 0)) 0))
    input-clean-capitalized))

第二:你可以像这样解决问题:

(defun underscore-to-camel-eff (input)
  (declare (optimize (debug 1) (speed 3) (safety 1)))
  (loop :with length = (length input)
        :with i = 0
        :while (< i length)
        :for c = (aref input i)
        :if (or (= i (- length 1))
                (char/= c #\_))
        :collect (prog1 c (incf i)) :into result
        :else
        :collect (prog1
                   (char-upcase (aref input (+ i 1)))
                   (incf i 2))
        :into result
        :finally (return (concatenate 'string result))))

使用SBCL在我的电脑上运行,只需要一半的解决时间。

这是一个使用正则表达式的解决方案,虽然比任何其他解决方案都慢:

(defun underscore-to-camel-ppcre (input)
  (declare (optimize (debug 1) (speed 3) (safety 1)))
  (ppcre:regex-replace-all "_([a-z])"
                           input
                           (lambda (target-string
                                    start
                                    end
                                    match-start
                                    match-end
                                    reg-starts
                                    reg-ends)
                             (declare (ignore start
                                              end
                                              match-end
                                              reg-starts
                                              reg-ends))
                             (string
                              (char-upcase
                               (aref target-string (+ 1 match-start)))))))

必要的包称为“ppcre”。 您可以通过

安装它
(ql:quickload "cl-ppcre")

一旦你去了http://www.quicklisp.org/beta/并安装了quicklisp。

答案 1 :(得分:2)

我建议使用字符级功能。他们从char-开始。然后你可以摆脱STRING-DOWNCASE和&#34; CONCATENATE`。

DEFPARAMETER不用于局部变量。使用LET

但是一个简单的版本是这样的:

(defun underscore-to-camel (input)
  (string-downcase (remove #\_ (string-capitalize input))
                   :start 0
                   :end 1))

答案 2 :(得分:2)

另一种方法:

(defun underscore-to-camel (input)
  (with-output-to-string (s)
    (loop
       :for c :across input
       :for upcase := (char= c #\_) :then (or upcase (char= c #\_)) :do
       (cond
         ((char= c #\_))
         (upcase (write-char (char-upcase c) s) (setf upcase nil))
         (t (write-char c s))))))

答案 3 :(得分:2)

用SBCL试验一段时间后,这是我找到的最快的版本

(defun camelcase (s)
  (do* ((n (length s))
        (i 0 (the fixnum (1+ i)))
        (wp 0)
        (target (make-array n :element-type 'character)))
      ((>= i n) (subseq target 0 wp))
    (declare (fixnum n i wp)
             (string s))
    (if (and (< i (the fixnum (1- n)))
             (char= (char s i) #\_)
             (char>= (char s (the fixnum (1+ i))) #\a)
             (char<= (char s (the fixnum (1+ i))) #\z))
        (setf (aref target (1- (the fixnum (incf wp))))
              (code-char (- (char-code (char s (the fixnum (incf i)))) 32)))
        (setf (aref target (1- (the fixnum (incf wp))))
              (char s i)))))

而不是调用#'char-upcase我只是减去32,因为已知该字符位于a - z范围内,而我正在假设ASCII编码。这削弱了一些周期。

由于某些原因,我不明白显式数组填充比使用vector-push更快。