【问题标题】:Lisp performance optimal solution in string processingLisp在字符串处理中的性能优化方案
【发布时间】:2013-08-03 15:54:40
【问题描述】:

我有一个用下划线分隔单词的字符串(例如 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)))

【问题讨论】:

  • 为什么不简单地使用正则表达式替换\([a-z]\)_\([a-z]\)\1\,(upcase \2)
  • 我正在使用 lispbox(Clozure Common Lisp)。默认情况下,正则表达式不可用。你建议哪个库支持upcase
  • 您可能希望正确缩进代码。然后defparameter 不会那样使用。 defparameter 用于全局函数
  • 感谢所有帮助过我的人。

标签: string performance lisp common-lisp


【解决方案1】:

首先,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 的 PC 上运行,只需您解决方案的一半时间。

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

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

【讨论】:

    【解决方案2】:

    我建议使用字符级函数。它们以char- 开头。然后你可以摆脱STRING-DOWNCASE和“CONCATENATE`。

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

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

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

    【讨论】:

      【解决方案3】:

      另一种方法:

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

      【讨论】:

        【解决方案4】:

        在用 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)))))
        

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

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

        【讨论】:

          猜你喜欢
          • 2016-06-26
          • 1970-01-01
          • 2020-11-30
          • 1970-01-01
          • 1970-01-01
          • 2011-04-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多