3
24
2012
10

Ruby 中 flip-flop 表达式的真谛:JK 触发器

今天看 Matz 的《Ruby编程指南》,遇到一个被称为「flip-flop」的奇特表达式:

在一个由条件式或循环所构成的上下文中,一个 flip-flop 由两个通过..操作符相连的布尔表达式构成。除非其左侧表达式为 true,否则一个 flip-flop 表达式就是 false,而且在左侧表达式为 true 之前,它的值都会是 false。一旦该表达式为 true ,那么它就会“flips”到一个持久的 true 状态。它会保持该状态,而且对其后续的求值也返回 true,直到其右侧表达式成为 true 为止。如果其右侧表达式为 true 了,那么该 flip-flop 就会“flops”回一个持久的false状态,对其后续的求值也返回false,直到其左侧表达式再次成为 true 为止。

[...]

Flip-flop 是一个非常晦涩的 Ruby 特性,因此最好不要在你的代码中使用它。但是它们并不是 Ruby 所独有的,Ruby 从 Perl 那里继承了这个特性,而 Perl 则从 Unix 的文本处理工具 sed 和 awk 那里继承了这个特性(注4)。Flip-flop 的初衷是在一个开始模式和一个结束模式之间匹配一个文本文件的行,而且这仍然是使用它们的有效方式。下面的这个简单的 Ruby 程序展示了一个 flip-flop,它逐行地从一个文本文件中读取内容,打印出含有“TODO”的行。它会不断地打印文本行,直到读入一个空行为止:

ARGF.each do |line|  # For each line of standard in or of named files
  print line if line=~/TODO/..line=~/^$/ # Print lines when flip-flop is true
end

作者还说很难正式地描述一个 flip-flop 的精确行为。但我不这么认为。

我想到了学习数字电路时遇到的各种触发器,于是乎,反复读了几次以上描述确定了 flip-flop 的行为到底如何后,列出了它的真值表,其中 A、B 为点两边的表达式的值(输入),Q 为其内部状态,Qn 就是 Qnext

A   B   Q   Qn
0   0   0   0
0   0   1   1
0   1   0   0
0   1   1   0
1   0   0   1
1   0   1   1
1   1   0   1
1   1   1   0

然后和那些触发器的真值表对照,赫然发现它就是JK 触发器——

J   K   Q   Qn
0   0   0   0
0   0   1   1
0   1   X   0
1   0   X   1
1   1   0   1
1   1   1   0

看来 Matz 没学过数字电路 ;-)

PS: flip-flop 在电子电路中就是「触发器」的意思。。。

Category: 编程 | Tags: ruby 数字电路
9
28
2010
2

Vim73之ruby $curbuf.number恒为零问题终于解决了!

上回说到Vim7.3在种种优点背后隐藏着种种bug,今天,其中之一终于被解决了!

Command-T 的作者在 vim_use 上的bug报告贴无人回应之后若干天,又在 vim_dev 上发了贴,今天终于有人回应了,指出configure时加上--disable-largefile选项此bug就消失了。我于是迅速地重新编译了下,果然OK了!

Category: Vim | Tags: vim ruby
9
3
2010
5

Vim7.3: 爱你不容易

Vim7.3b出来后我就急不可耐地体验了把,但因为它的ruby支持中$curbuf.number始终返回零,不仅影响到了Command-T插件,也严重影响了我非常喜欢+常用的LustyExplorer插件——时不时地SEGSEGV一下可不行。

等正式版终于出来后,我又立即下载编译了,结果问题依旧。无奈,我只好自己动手,把LustyExplorer中用到$curbuf.number的地方都换成VIM::evaluate('bufnr("%")')了,但之后还是在调用此插件时经常SEGSEGV,郁闷了,于是换回Vim7.2。

但是Vim7.3真的有些很好的特性啊。此前介绍过的就不重复了,下面说说后来发现的两个我很喜欢的特性。

strwidth()函数

strlen()大家都知道,但它求的是字符串的字节数,而不一定等于字符串的宽度,比如中文就是如此。这会造成无法将内容对齐。于是,用于对齐代码的Align插件在处理CJK字符时(设置let g:Align_xstrlen = 3时)使用了一种很耗时的办法来求字符宽度:将字符串显示在Vim里,然后看它占用了多少虚拟列。

有了Vim7.3的strwidth()等函数就不必这么麻烦了。我去改了相关代码,执行效率提升N倍。

setf补全

setf是设置文件类型的指令。这个我很常用,比如写php文件的HTML部分时为了正确地缩进HTML,我会把文件类型设置成HTML,写PHP部分时还要切回去。写新脚本时由于通常没有文件扩展名,所以Vim无法识别文件类型。这些情况我只好手动:setf xxx了。Vim7.3增加了对此的补全支持,可以少敲点字母了。


所以——

所以为了用上Vim7.3,同时又不放弃LustyExplorer插件,我前不久又开始折腾Vim7.3的源代码,最后连gdb都用上了,在段错误时得到以下backtrace结果:

#0  __memset_sse2 () at ../sysdeps/i386/i686/multiarch/memset-sse2.S:160
#1  0x00d1d6ac in Perl_sv_upgrade () from /usr/lib/libperl.so.5.10
#2  0x00d1ea6a in Perl_sv_magicext () from /usr/lib/libperl.so.5.10
#3  0x00d1fc11 in Perl_sv_magic () from /usr/lib/libperl.so.5.10
#4  0x00d21882 in Perl_sv_setiv () from /usr/lib/libperl.so.5.10
#5  0x08203691 in perl_buf_free (bp=0x847b890) at if_perl.xs:638
#6  0x080742e3 in free_buffer (buf=0x847b890) at buffer.c:613
#7  0x08073fec in close_buffer (win=0x0, buf=0x847b890, action=4)
    at buffer.c:468
#8  0x08074de8 in do_buffer (action=4, start=0, dir=1, count=0, forceit=1)
    at buffer.c:1136
#9  0x0807465e in do_bufdel (command=4, arg=0x84b8aa1 "", addr_count=0, 
    start_bnr=12, end_bnr=1, forceit=1) at buffer.c:834
#10 0x080cbb2c in ex_bunload (eap=0xbfff9f8c) at ex_docmd.c:4939
#11 0x080c817d in do_one_cmd (cmdlinep=0xbfffa140, sourcing=1, 
    cstack=0xbfffa148, fgetline=0, cookie=0x0) at ex_docmd.c:2656
#12 0x080c5a56 in do_cmdline (cmdline=0x837c2d0 "bwipeout!", getline=0, 
    cookie=0x0, flags=11) at ex_docmd.c:1122
#13 0x080c5110 in do_cmdline_cmd (cmd=0x837c2d0 "bwipeout!") at ex_docmd.c:728
#14 0x0820cf98 in vim_command (self=3084803600, str=3084486540) at if_ruby.c:731
#15 0x010619a3 in ?? () from /usr/lib/libruby1.8.so.1.8
---Type  to continue, or q  to quit---

接着我很快发现了问题——明明是个ruby写的插件,最上面怎么调用到Perl了呢?遂关闭+perl特性重新编译,几天已经过去了,Vim7.3依旧运行良好,没有再出现以下烦人的提示了:

Vim: 拦截到致命信号(deadly signal) SEGV
Vim: 结束。
zsh: segmentation fault  vim

现在我一直在关注 ftp://ftp.vim.org/pub/vim/patches/7.3/,期待着这个问题从根本上解决……

Category: Vim | Tags: vim ruby

Mastodon | Theme: Aeros 2.0 by TheBuckmaker.com