Aiur

ZelluX 的技术博客

转换 Visio 图片为 EPS 格式

Windows 7 及 Visio 2010 下验证可行:

  • 添加本地打印机,类型选择 Generic -> MS Publisher Color Printer
  • 在 Visio 中打印,选择新添加的打印机,选中 Print to file,点 Properties -> Advanced
  • Document Options -> PostScript Options -> PostScript Output Option 中,选择 Encapsulated PostScript (EPS)
  • 保存时别忘了加后缀名 .eps

这样输出的 EPS 文件的边框是整个 A4 页面,需要用 epstool 把它们周围的白边框去掉:

1
epstool --copy --bbox figure.eps figure-fixed.eps

epstool 在常见的几个软件源中应该都能找到。

一些 ZvT 的心得

一直在国服大师组混,最让我头疼,同时研究的也最多得对战就是 ZvT 了。这里写一点自己关于 ZvT 的心得,欢迎探讨。

前期

  1. 探路农民不能少,我一般在 14d 的时候拉个农民出去探路。探到对方位置后农民别急着走,要在对方路口停留一会儿,注意对手的第一个枪兵有没有跑出来,以便做好防 rush 的准备。同时这也避免了对方农民在你基地旁造一个欺骗性的地堡时,拉过多的农民下来防守。

  2. 分矿处女王造出来后第一件事是铺菌毯而不是补虫卵。第三个女王也一定要出,保证菌毯铺开的速度。

  3. 防火车的话,我现在还是倾向于用蟑螂。用蟑螂的另一个好处在于配合小狗应付对手第一波坦克+枪兵的压制比较有效,第一波出来的时候自爆速度往往还没有好,在没有菌毯的地方很难起效果。

  4. T 开局还有几个变种,如果看到对方出了火车,数量不多,而且其他地面部队也不多的话,还是老老实实补个 bv 造防空吧。

中期

  1. 还是关于侦查。对方二矿开始运作后,小狗要时不时的看看对方家门口。一要注意对手兵力组合,二要注意攻防。如果枪兵不多也没升攻防,对手就有可能用机械化部队了。

  2. 如果对手是标准的枪兵+坦克的组合,我倾向于防下对手第一波部队或者自己飞龙出来后再开三矿。不要因为对手开矿早,就以为自己也能随意补农民发展经济了。现在 T 第一波的 timing 都抓得蛮准,三矿农民补早了很有可能第一波就被推掉了。

  3. 相反,打机械化组合就要利用对方部队成形前的真空期尽早开矿,这也是前面强调小狗侦查的原因。

  4. 开三矿后记得在主矿分矿上都码上几个地堡,这点可以好好像雀茶学学。T 空投你的目的不单单是骚扰经济,而是让自己的主力部队能舒舒服服的推到合适的位置。防下对手一船空投后发现对手已经在咽喉位置架好坦克,摆好枪兵阵形了,这时候就很难打下来。

  5. 对方坦克阵慢慢推进的时候切记一定要耐心,对手总能出现疏漏的。同时飞龙记得吃掉落单的补给部队。

  6. 小狗+自爆和对方枪兵+坦克打正面的时候,能包最好包,不能包也要记得拉一队小狗拦住枪兵,干扰走位。

  7. 怎么打机械化。发现得早的话我一般就做好龙狗换家的准备了,成功率也不低。但是发现晚的话,只能硬着头皮打正面了。现在 GSL 上比较常见是在对手刚出家门,坦克还没架起的时候,用蟑螂吸引火力,同时大量的自爆上去换雷神。我试了几次效果不是很好,可能是时机没选择好的缘故吧。

  8. 飞龙一定要保存好。攒多了威慑力非常大,能很有效的牵制对手的部队。

  9. 天梯上有时还能看到另一个比较奇葩的战术:爆维京。听起来很不靠谱的战术,但是实战中经常能把人打懵(Z 人口补不上,对方维京成型后地面骚扰能力也很强)。当然这种战术应对起来也不难,多预留点人口,每个分矿都码几个堡,准备一两队小狗,接着出飞龙就好了。类似的战术还有火车女妖流,都是能打得你很不舒服,但是一旦放下来就没啥威力的战术。

后期

  1. 我觉得 ZvT 后期的关键在于防空投,因为自爆+狗+感染+母巢王虫的组合基本不怎么怕 T 打正面。但是三攻三防的枪兵拆分矿的能力很强,对应方法,也就只有放好领主侦查,同时在分矿补更多的堡了。

  2. 出母巢王虫后记得拉上所有的女王,加血效果会让对手很无语。

其他补充

一个女王加虫卵的技巧。星际2里有一个切换主基地的快捷键,默认是Backspace,我把它设置成了Tab上边那个键。把所有女王编队后,按住Shift,点切换键,按v后再点一个基地,再按切换键,再v一个基地,这样一轮循环下来可以给所有基地注上虫卵。

不过这么做有一个问题就是女王数量比基地数少的时候,会出现女王到处跑的现象。我现在用的方法是不按Shift,点切换键快速切到基地视角,如果这个基地旁有女王就控制它注卵。这样操作上麻烦了一点,而且注卵时间上有个判断的延迟,但是灵活性高了不少。

转用 octopress 了

jekyll 是一个静态博客生成工具,可配置性很强。但是它的配置对于初学者不是很友好,没有现成的模版,需要自己从头搭一个。octopress 大大简化了这一配置过程,在 jekyll 的基础上提供了一个默认主题,以及一些常用的插件。

Why

在 github 上捣鼓了一阵子 octopress 后,决定把原来的 wordpress 博客的数据转移到这个 octopress 博客上了。相对于 wordpress,octopress 的优点在于:

  • 支持 Markdown 语法。Markdown 是 github、stackoverflow 上的默认标记语言,写笔记我也一直用这个。Mac 平台上有不少好用的 Markdown 编辑器,例如收费的 Byword,免费的 Mou,这些工具都增加了写日志时的愉悦感。

  • 静态。对主页空间没有要求,甚至放到 github pages 上都可以。静态页面如果要加评论,可以考虑 disqus 等第三方 JS 工具。

  • 对内嵌代码支持很好。内置了 pygments ,这里有一份支持语言的列表。值得一提的是 octopress 还支持内嵌 Gist。

  • 日志文件都在本地,而且是纯文本,管理很方便(可以用 git),也不用担心租用的服务器数据丢失等问题。

  • rake new_post; rake gen_deploy 这样写博客很过瘾 :)

How

关于 wordpress 到 octopress 的数据转移,本文结尾的两篇参考文章已经说得很详细了,这里再补充几点:

  • 编码:jekyll 的 wordpressdotcom.rb 用了 yaml 库生成博客文章的 meta 信息,碰到中文标题会出现乱码,换用 ya2yaml 后问题解决。

  • 博客图片:把原来的 wp-content 目录复制过来,再统一改下路径即可。

  • 文章格式:wordpress 导出的文章内容格式比较特殊,不是标准的 HTML,因为它的换行都是有意义的,考虑到这点我就把文章保存成 .markdown 后缀了,效果也不错。Vito 的博客上还介绍了 downmark_it 这个把 HTML 转成 Markdown 的工具。

  • 评论:用了 disqus,它还支持导入 wordpress 上过去的评论。

  • 代码高亮:之前博客用的是一个基于 JS 的 SyntexHighlighter,octopress 自带了 pygments 作为语法高亮工具,两者高亮标记不一样,需要用 nokogiri 转换下。

  • RSS:虽然 octopress 自带了生成 Atom 的插件,但是只能生成一个,而之前博客的 /feed/ 和 /rss.xml 都有人订阅,所以得在 nginx 配置里加了几条重写规则保证这些 URL 都有效。

  • 主题:octopress 支持用 SCSS 自定义主题。现在这个用的主题还是默认的,改天再考虑要不要把原来的主题也移植过来。

  • 写作:建议在 Rakefile 的 new_post 方法结尾启动 Markdown 编辑器打开新生成的文件,这样就免去手动查找的麻烦了。

这个是修改后的 wordpressdotcom.rb,根据我的博客的情况加了一些特殊情况的处理,有同样需求的朋友可以参考下。

参考:

ActiveRecord 的一些细节

对象属性

ActiveRecord 对象在数据库中的属性并不是以实体变量的方式保存的,如果要为一个属性设置默认值的话,

1
2
3
4
5
class Item < ActiveRecord::Base
  def category
    @category || 'n/a'
  end
end

这样的实现是不可行的。读取和修改这些属性时应该使用 read_attribute 和 write_attribute:

1
2
3
4
5
class Item < ActiveRecord::Base
  def category
    read_attribute(:category) || 'n/a'
  end
end

Hash 和相等性

ActiveRecord 的 hash 值是根据主键的值计算出来的,这就意味着未保存对象的 hash 值是不可靠的。同样两个 model 对象的相等比较(即==操作符)也是基于主键的,所以两个 model 对象即使它们的其他属性不一样,仍有可能被当作相等。

查找

find_by_attribute 方法后面加个 ! 号,即使用 find_by_attribute!,就能在找不到对象的时候触发一个 RecordNotFound 异常,而不是返回 nil。

find_or_initialize_by 和 find_or_create_by 也是两个好用的方法,它们在找不到对象时分别使用 new 和 create 新建一个,并用查找的属性初始化新建的对象。

手写 SQL

不得不手写 SQL 同时又要防止注入攻击的一个比较简洁的写法是

1
Order.where("name = :name and pay_type = :pay_type", params[:order])

回调函数

出于性能考虑,after_find 和 after_initialize 只能通过函数声明的方式定义,即不能用类似 before_validation :normalize_fields 这样的形式。

参考

REST 服务的方法

HEAD 方法和 GET 方法比较像,但是它不返回对象的实际表示,只返回一个 HTTP 头。HEAD 可以用来查看对象修改时间、大小等信息,Amazon S3 的客户端就用它来读取文件元信息。

用 PUT 和 POST 创建对象时的一个区别在于,使用前者时客户端知道被创建对象的 URL(例如 /items/3),而后者则不需要客户端了解(例如 /items/new)。

OPTIONS 用来查看客户端对某个资源有那些可用的操作。

正确的设计应当保证:

  • GET 和 HEAD 是安全的,即不会修改任何对象状态。多次调用它们的结果应当和只调用一次甚至不调用一样。
  • GET、HEAD、PUT 和 DELETE 方法是幂等(idempotent)的。多次调用它们的结果应当和只调用一次一样。

这两点保证了在一个不可靠的网络中,客户端仍能进行有效的操作。

参考:Restful Web Services

render 方法的可选参数

:content_type 设置返回内容的 MIME 类型

1
render :file => filename, :content_type => 'application/rss'

:layout 指定 layout

:status 指定返回的 HTTP 代码

:location 指定 HTTP 头中的 Location 字段

Rails 生成的 controller 代码中,create.json 方法在生成对象后会将 Location 设置为新生成对象的 json 地址:

1
2
3
4
5
6
7
8
9
respond_to do |format|
  if @item.save
    format.html { redirect_to @item, notice: 'Item was successfully created.' }
    format.json { render json: @item, status: :created, location: @item }
  else
    format.html { render action: "new" }
    format.json { render json: @item.errors, status: :unprocessable_entity }
  end
end

参考:http://guides.rubyonrails.org/layouts_and_rendering.html#using-render

rspec 跳过指定测试

有些测试比较耗时间,而且很少被修改,如果能在测试的时候跳过它们就能让 spec 快不少。

跳过测试的方法很简单,spec 的 describe 方法可以给对应的测试加上标签,例如

1
2
3
describe SalesController, :slow => true do
    # specs
end

接下来只要在 spec/spec_helper.rb 中声明跳过这个标签即可:

1
2
3
RSpec.configure do |config|
  config.filter_run_excluding :slow => true
end

与 filter_run_excluding 相反的是 filter_run,指定会被运行的标签,不包含在这个列表中的测试将被忽略。

参考:http://www.dixis.com/?p=283

一个简单有效的 hash 算法

最近要给某个类写一个 hash 方法,这个类包括一些整型和字符串属性,需要把它们都放到 hash 中。担心自己想出来的 hash 算法会造成比较严重的冲突,网上搜了一下,发现 Effective Java 中已经介绍过一种简单有效的算法了:

  1. 将任一非零常数赋值给 result
  2. 找到该类中所有需要包含在 hash 中的属性,并根据它们的类型进行计算 c
    1. 对于布尔类型,将它们转换为 0/1
    2. 对于 byte, char, short, 和 int 类型,将它们转换为 int
    3. 对于长整型 long,计算高位和低位的异或结果 (int) (f ^ (f >>> 32))
    4. 对于 float 类型,采用它的二进制表示,在 Java 中为 Float.floatToIntBits,Ruby 中我估计用 Float.hash 也可以
    5. 对于 double 类型,调用 Double.doubleToIntBits 后再次用前面的方法处理得到的 long 类型
    6. 对于数组,利用方法 3 合并计算结果
    7. 对于其它的对象,递归调用该对象的 hash 方法
  3. 将计算结果合并到 result 变量:result = 37 * result + c
  4. 返回 result

另外这个博客的域名已经改成了 blog.yxwang.me,原来的域名 techblog.iamzellux.com 仍然可用,但会重定向到新的域名。

让 Emacs 支持 Lion 的全屏模式

前几天给我的 MacBook Pro 装上了 Lion,不过原来的 Emacs 并不支持在 Lion 下全屏运行。github 上搜了下发现已经有让 Emacs 支持全屏模式的补丁了,Homebrew 中这个补丁也已经被吸收

直接用 brew install emacs --cocoa --srgb 似乎会碰到编译错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Finding pointers to doc strings...
Finding pointers to doc strings...done
Dumping under the name emacs
unexec: cannot write section __data
--- List of All Regions ---
   address     size prot maxp
--- List of Regions to be Dumped ---
   address     size prot maxp
--- Header Information ---
Magic = 0xfeedfacf
CPUType = 16777223
CPUSubType = -2147483645
FileType = 0x2
NCmds = 20
SizeOfCmds = 3464
Flags = 0x00200085
Highest address of load commands in input file: 0x5dd000
Lowest offset of all sections in __TEXT segment:   0x22f0
--- List of Load Commands in Input File ---

github issues 上已经有人报告这个问题了,解决方法也很简单,运行 brew edit emacs 打开 emacs 的安装脚本,在 def install 的后面加上两行:

1
2
3
4
5
6
7
8
  def install
    ENV['CFLAGS']='-fno-pie -O2'
    ENV['LDFLAGS']='-fno-pie'
    args = ["--prefix=#{prefix}",
            "--without-dbus",
            "--enable-locallisppath=#{HOMEBREW_PREFIX}/share/emacs/site-lisp",
            "--infodir=#{info}/emacs"]
    # ...

再运行一次 brew install emacs,就能在 /usr/local/Cellar/emacs/23.3 下找到支持全屏模式的 Emacs.app 了。M-x ns-toggle-fullscreen 可以在全屏/非全屏模式之间切换。

利用 netgrowl 向 Windows / Mac OS X 发送消息

我平时用的系统是 Windows 7 和 Mac OS X,实验室项目一般都是 ssh 远登到 Ubuntu 和 Linux 上开发的。有时碰到内核和虚拟机等项目编译比较耗时,编译开始后要时不时的看一下编译任务是否完成,或者有没有中途出错,这时候如果有个通知系统就比较方便了。

Google 了一把找到了 netgrowl 这个好东东,它是一个开源的 Python 模块,实现了 Growl 协议,可以向 Mac 或 Windows 上的 Growl 服务发送通知。使用也非常方便,先用 GrowlRegistrationPacket 函数注册一个应用,接着就可以用 GrowlNotificationPacket 发送通知了:

notify.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python

from netgrowl import *
import sys

title = "Notification from Ubuntu"
desc = ""
if len(sys.argv) > 2:
    title = sys.argv[1]
    desc = sys.argv[2]

addr = ("10.131.251.101", GROWL_UDP_PORT)
s = socket(AF_INET,SOCK_DGRAM)
p = GrowlRegistrationPacket(application="Ubuntu", password="i")
p.addNotification("Ubuntu Notifications", enabled=True)
s.sendto(p.payload(), addr)
p = GrowlNotificationPacket(application="Ubuntu",
    notification="Ubuntu Notifications", title=title,
    description=desc, priority=1,
    sticky=True, password="i")
s.sendto(p.payload(),addr)
s.close()

这里的 addr 是接收方的地址,GrowlRegistrationPacket 和 GrowlNotificationPacket 中需要指定 Growl 远程服务的密码。

然后是一个简化 notify.py 调用的 shell 脚本:

growl.sh

1
2
3
4
5
#!/bin/bash

cmd=$@
$cmd
python ~/bin/notify.py Done "$cmd under $PWD is finished"

把 growl.sh 加入到 PATH 中,之后只要运行 growl.sh make all 就能运行 make all 命令 ,并且在执行完成后向 Growl 客户端发送消息了。如果安装了 BoxCar,还能把这条消息转发到 iPhone / iPad 上。

P.S. Growl for Windows 可以在这里找到。