利用hashtable和time函数加速Lisp程序
来源:互联网 发布:编程好找工作吗 编辑:程序博客网 时间:2024/06/06 02:17
程序功能是从一个英文文本中得到单词表,再得到押韵词表。即输出可能这样开始:
a ameoeba alba samba marimba...
这样结束:
...megahertz gigahertz jazz buzz fuzz
有了这么一个表,诗人会爽很多。
(setf words (delete-duplicates words :test #'equal :end n))
用delete-duplicates函数只保留相同单词的最后一个。
该函数的工作原理是两个两个比较,然后把前者丢弃掉,如果:from-end是true的话,那把后者丢弃掉。显然的是,它要比较很多,如果一个单词重复很多次的话,那要经过很多轮的比较,元素移动很多。具体是如何经过很多轮的、为什么很慢的,我也不懂,望高手指点。
如果我在添加新单词的时候:
这样子给每个函数time一下。在c++中,即是clock()/CLOCKS_PER_SEC。有了它,就是为啥“过早优化是一切罪恶的根源”。非常令我惊讶的是,一开始总共37s的时间,36s耗在 delete-duplicates上。按道理来说,我应该用36/37的优化时间都花在这部分。如果过早优化的话,一方面浪费时间在无关痛痒的优化上;另一方面,过早优化,代码更复杂,使得后面越来越难以修改,也容易多错误。所以,面对一问题,应该尽快地捣鼓出简单的第一版,之后再考虑优化。
接下来,用hash-table来记录单词,像这样
希望数组v每个数都加1可写成:
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
- 利用hashtable和time函数加速Lisp程序
- 利用cuda加速MATLAB程序
- 利用多线程加速程序的小程序
- Lisp.函数
- Lisp.函数
- Lisp 函数
- Lisp函数apply和funcall的比较
- 利用Session和HashTable制作购物车
- c# 利用Time实现定时启动程序
- LISP 学习笔记 (三) -编辑,加载和编译LISP程序
- 用ASDF来组织Lisp程序编译和加载
- lisp小程序,useless
- 比较大小lisp程序
- Lisp运行程序
- 测量lisp小程序
- linux time 和 date 函数
- 标准函数time()和localtime()
- Lisp 匿名递归函数
- 以天徒龙记
- Python2.7与Python3.3不同而产生的错误
- Java程序员必知的8大排序
- jQuery数据缓存-queue([name], queue)
- 为什么初创公司需要一个明晰表述的发展战略?
- 利用hashtable和time函数加速Lisp程序
- 【重点】引用在栈内存和堆内存之间的巧用
- Java中大数初级使用
- 在JIRA的一个项目中,如何设置让项目中的问题可以设置不同的权限让不同的人看到。
- C++内联函数跟普通函数的区别以及实现机制
- C++语言实现代码注释的自动删除
- adb常用命令
- POJ 1321 chessboard-problem
- uva 1356 Bridge ( 辛普森积分 )