程序功能是从一个英文文本中得到单词表,再得到押韵词表。即输出可能这样开始:
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程序的更多相关文章

  1. 给Lisp程序员的Python简介

    给Lisp程序员的Python简介 作者:Peter Norvig,译者:jineslong<zzljlu@gmail.com> 这是一篇为Lisp程序员写的Python简介(一些Pyth ...

  2. 小议common lisp程序开发流程 - Ever 17 - 博客频道 - CSDN.NET

    小议common lisp程序开发流程 - Ever 17 - 博客频道 - CSDN.NET 小议common lisp程序开发流程 分类: lisp 2011-04-17 20:59 1316人阅 ...

  3. [转载]高效使用matlab之四:一个加速matlab程序的例子

    原文地址:http://www.bfcat.com/index.php/2012/11/speed-up-app/ 这篇文章原文是matlab网站上的,我把它翻译过来同时自己也学习一下.原文见这里 这 ...

  4. 用Cython加速Python程序以及包装C程序简单测试

    用Cython加速Python程序 我没有拼错,就是Cython,C+Python=Cython! 我们来看看Cython的威力,先运行下边的程序: import time def fib(n): i ...

  5. 利用Costura.Fody制作绿色单文件程序(C#程序(含多个Dll)合并成一个Exe)

    原文:利用Costura.Fody制作绿色单文件程序(C#程序(含多个Dll)合并成一个Exe) 开发程序的时候经常会引用一些第三方的DLL,然后编译生成的exe文件就不能脱离这些DLL独立运行了.这 ...

  6. 利用InstallShiled 10.5制作AE应用程序安装包

    [转]利用InstallShiled 10.5制作AE应用程序安装包 作者:3SNEWS 社区ESRI(ArcGIS)版版主:zhaoxiang_whuhttp://www.3snews.net/bb ...

  7. 使用RawComparator加速Hadoop程序

    使用RawComparator加速Hadoop程序 在前面两篇文章[1][2]中我们介绍了Hadoop序列化的相关知识,包括Writable接口与Writable对象以及如何编写定制的Writable ...

  8. [05] 通过P/Invoke加速C#程序

    通过P/Invoke加速C#程序 任何语言都会提供FFI机制(Foreign Function Interface, 叫法不太一样), 大多数的FFI机制是和C API. C#提供了P/Invoke来 ...

  9. 利用Gitee转接GitHub下载加速 简简单单 - 快快乐乐

    利用Gitee转接GitHub下载加速 简简单单 - 快快乐乐 JERRY_Z. ~ 2020 / 10 / 26 转载请注明出处!️ 目录 利用Gitee转接GitHub下载加速 简简单单 - 快快 ...

随机推荐

  1. hdu 4607 Park Visit(树上最长链)

    求树上最长链:两遍搜索. 第一次从树上任意点开始,最远点必然是某一条最长链上的端点u. 第二次从u开始,最远点即该最长链的另一端点. 先在最长链上走,不足再去走支链. 把询问数m错打成n,狠狠wa了一 ...

  2. UVA 1663 Purifying Machine (二分图匹配,最大流)

    题意: 给m个长度为n的模板串,模板串由0和1和*三种组成,且每串至多1个*,代表可0可1.模板串至多匹配2个串,即*号改成0和1,如果没有*号则只能匹配自己.问:模板串可以缩减为几个,同样可以匹配原 ...

  3. jquery插件——日历控件

    今天在网上有看到一个jquery插件——日历控件,不过之前也在柯乐义的网站上看到了(http://keleyi.com/ 推荐下) 这个插件看着比较大气,所以干脆也分享下,以后自己也好用一点儿 1.页 ...

  4. MyBatis学习 之 二、SQL语句映射文件(1)resultMap

    目录(?)[-] 二SQL语句映射文件1resultMap resultMap idresult constructor association联合 使用select实现联合 使用resultMap实 ...

  5. ASP.NET CORE Web浏览器和Web服务器

    //web浏览器 //浏览器本质的原理:浏览器向服务器发请求,服务器把请求的内容返回给浏览器,然后浏览器把返回的内容绘制成一个图形化的界面 //Socket一种通讯交流的技术 //qq用户把信息通过s ...

  6. Ubuntu消息菜单(MessagingMenu)API

    应用程序可以注册在消息菜单里显示消息,它也可以使用全局聊天状态项目. 注册 应用程序要在消息菜单里显示消息,必须满足以下条件: $HOME/.config/indicators/messages/ap ...

  7. iOS多线程之GCD小记

    iOS多线程之GCD小记 iOS多线程方案简介 从各种资料中了解到,iOS中目前有4套多线程的方案,分别是下列4中: 1.Pthreads 这是一套可以在很多操作系统上通用的多线程API,是基于C语言 ...

  8. redo文件二

    为什么要引入LGWR后台进程和redo log buffer 如果使用前台进程来将redo的信息写入到redo日志文件组中,那么会导致并发的前台进程对redo日志文件组的争用,从而使用后台进程LGWR ...

  9. JQuery WEB前段开发

    JQuery WEB前段开发 Jquery是继prototype之后又一个优秀的Javascript框架.它是轻量级的js库 ,它兼容CSS3,还兼容各种浏览器(IE 6.0+, FF 1.5+, S ...

  10. bzoj 3365 [Usaco2004 Feb]Distance Statistics 路程统计(点分治,单调)

    [题意] 求树上长度不超过k的点对数目. [思路] 和 Tree 一样一样的. 就是最后统计的时候别忘把根加上. [代码] #include<set> #include<cmath& ...