zoukankan      html  css  js  c++  java
  • 利用hashtable和time函数加速Lisp程序

       程序功能是从一个英文文本中得到单词表,再得到押韵词表。即输出可能这样开始:
    a ameoeba alba samba marimba...
    这样结束:
    ...megahertz gigahertz jazz buzz fuzz
    有了这么一个表,诗人会爽很多。

    算法:得到单词表后,先把单词反序,用sort函数对所有反序单词排序,排好后再反序。

         我叽里呱啦地写完了,用了630KB的2100行的文本来测试一下。time一下main函数,30多秒……现在用了如题的两个工具,程序跑到了0.08s!

    首先,我处理重复单词的算法超级低效,先是在read-word函数里,不管是否当前单词已记录,都把当前单词记录下来,然后:
    (setf words (delete-duplicates words :test #'equal :end n))
    用delete-duplicates函数只保留相同单词的最后一个。
    该函数的工作原理是两个两个比较,然后把前者丢弃掉,如果:from-end是true的话,那把后者丢弃掉。显然的是,它要比较很多,如果一个单词重复很多次的话,那要经过很多轮的比较,元素移动很多。具体是如何经过很多轮的、为什么很慢的,我也不懂,望高手指点。
    如果我在添加新单词的时候:
     

    (when (not (find word words :test #'string-equal))
      (vector-push-extend word words 10000)
      (incf n))

    单词不存在的时候才加入。这样一来,程序就飞到了4s。

    我是怎么知道delete-duplicates这里慢了呢?高手可以静态分析……当然,很容易猜错,分析失误。

    (defun main ()
       (defvar n)
       (time (setf n (read-word "source.txt")))
       (time (setf words (delete-duplicates words :test #'equal :end n)))
       (time (reverse-words words))
       (time (sort words #'string< #'nreverse))
       (time (reverse-words words))
       (time (write-word "rhymes.txt")))


      这样子给每个函数time一下。在c++中,即是clock()/CLOCKS_PER_SEC。有了它,就是为啥“过早优化是一切罪恶的根源”。非常令我惊讶的是,一开始总共37s的时间,36s耗在 delete-duplicates上。按道理来说,我应该用36/37的优化时间都花在这部分。如果过早优化的话,一方面浪费时间在无关痛痒的优化上;另一方面,过早优化,代码更复杂,使得后面越来越难以修改,也容易多错误。所以,面对一问题,应该尽快地捣鼓出简单的第一版,之后再考虑优化。
    接下来,用hash-table来记录单词,像这样

    (defparameter ht (make-hash-table :test #'equal :size 10000))
    (when (not (gethash word ht)) 
      (setf (gethash word ht) t)
      (vector-push-extend word words 10000))
    

    这样就优化到了0.08s。

    另外,在处理的过程中,不希望有那么多中间数组,故用map-into,像这样

    (map-into seq fn seq)

    函数fn对seq的每一个元素调用后结果保存到seq中。
    希望数组v每个数都加1可写成:

    (setf v (map-into v #'1+ v))

    所以,最终我把反序、排序、再反序、输出,写成了这样:

    (defun xform (fn seq)
    	(map-into seq fn seq))
    (defun write-word (to)
    	(with-open-file (str to :direction :output
    				:if-exists :supersede)
    	  (map nil #'(lambda (x)
    			(fresh-line str)
    			(princ x str))
    		  (xform #'nreverse
    			(sort (xform #'nreverse words)
    			      #'string<)))))

    事实上,这照搬了《Ansi Common Lisp》的代码。

    代码见:https://github.com/lzwjava/rhymes
     

  • 相关阅读:
    Java核心技术 卷一 笔记四 库类的直接使用
    Java核心技术 卷一 笔记三 大数值及数组
    Java核心技术 卷一 笔记2 字符串的复制
    Java核心技术 卷一 笔记1
    修改css 样式后, hover事件 不生效
    修改 element ui input 输入框 样式不生效问题
    css3 计算属性
    Vue3 改动系列
    浏览器实现,向下滑动 鼠标滚轮,页面横向移动
    linux ceont0s7 vue 打包压缩图片 一直报错
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3367583.html
Copyright © 2011-2022 走看看