Monthly Archive for 七月, 2009

Page 2 of 2

简介协程

原文:Coroutines Tutorial
翻译:ssword

What are coroutines?
何为协程?

协程允许我们同时执行多个任务。将它们分派到不同的子程序(routine)中,在每个子程序表示完成时将控制权转移,并可以回到上次完成的位置继续执行,如之重复,从而实现并发。

在参考手册的2.11节5.2节中都有对协程的讲解。

Multi-threading
多线程

让每个任务独立地运行在一个线程中,同时执行多个任务就叫做多线程(multi-threading)。使用多线程的应用就叫做多线程应用(multi-threaded)。

多线程的实现方式多种多样,一些系统是为每个线程申请固定的时间,在时间结束时转移控制权到下一个线程。这叫做抢占式(pre-emptive)多线程。这种调度方式中,每个线程不必关心自己占据的时间,而更关注于自身的功能。

还有别的系统,线程知道自己占据的时间,也知道应自己在何时转让控制权给别的线程,来执行各自的功能。这叫做联合式(cooperative)或协作式(collaborative)多线程。应用程序中的所有线程都是协作在一起,这也正是lua协程使用的多任务方式。

Lua的协程既非操作系统线程,也非进程。它是在lua中创建的一块代码,与线程一样有自己的控制流程,不过在同一时刻只能运行一个协程。而且只有新协程被激活或有yield(返回到执行它的那个协程),才会转移控制权。协程就是表示协作式线程的一种简单方式,不过没有并行(execute in parallel),也就无法得到多核心CPU的性能优势。但是,由于协程在切换起来要比操作系统线程快得多,也不需要复杂甚至代价昂贵的锁机制,使用协程通常都要比等价的操作系统线程轻快一些。

Yielding

要让多个协程共同执行,就必须停止当前协程的执行(在执行一些操作之后),并转移控制权到另一个协程,这种操作就叫做yielding。协程可以直接调用个一个lua函数,coroutine.yield(),它与函数的return类似。使用yield退出函数的位置可以被记住,在稍后可以回到该位置接着刚才的上下文继续执行。不过若使用return退出,函数的整个上下文就被销毁了,我们也就无法回到该位置。

> function foo(x)
>>  if x>3 then return true end  -- we can exit the function before the end if need be
>>  return false                 -- return a value at the end of the function (optional)
>> end
> = foo(1)
false
> = foo(100)                     -- different exit point
true

Simple usage
简单的用法

要创建一个协程,得先有个表示它的函数。

> function foo()
>>   print("foo", 1)
>>   coroutine.yield()
>>   print("foo", 2)
>> end
>

使用coroutine.create(fn)函数可以创建一个协程,它的参数是个lua函数。它返回的类型为thread:

> co = coroutine.create(foo) -- create a coroutine with foo as the entry
> = type(co)                 -- display the type of object "co"
thread

我们可以用coroutine.status()函数来检查线程的状态。

> = coroutine.status(co)
suspended

状态suspended表示这个线程是可用的,而且如你所想,它还什么也没做。注意,在我们创建线程时,它不会立即执行。要执行它,我们使用corotine.resume()函数。Lua会进入这个线程,并在出现yield时离开。

> = coroutine.resume(co)
foo     1
true

corotine.resume函数返回了resume调用的错误状态。这输出表示了我们进入的是foo函数,退出时没发生错误。有趣的地方就在这里。单靠一个函数,我们不可能回到离开时的上下文继续执行,而协程则允许我们一次次地resume:

> = coroutine.resume(co)
foo     2
true

可以看出,这行代码回到了foo中上次yield的位置执行并返回,没有错误发生。不过如果看下它的状态,就可以看出我们退出了foo函数,协程也结束了。

> = coroutine.status(co)
dead

如果试图再次resume,就会返回两个值,一个错误标记和一条错误信息:

> = coroutine.resume(co)
false   cannot resume dead coroutine

一旦协程退出,或是像函数那样返回,它就无法执行resume了。

More details

下面是个复杂些的例子,展示协程的几个性质:

> function odd(x)
>>   print('A: odd', x)
>>   coroutine.yield(x)
>>   print('B: odd', x)
>> end
>
> function even(x)
>>   print('C: even', x)
>>   if x==2 then return x end
>>   print('D: even ', x)
>> end
>
> co = coroutine.create(
>>   function (x)
>>     for i=1,x do
>>       if i==3 then coroutine.yield(-1) end
>>       if i % 2 == 0 then even(i) else odd(i) end
>>     end
>>   end)
>
> count = 1
> while coroutine.status(co) ~= 'dead' do
>>   print('----', count) ; count = count+1
>>   errorfree, value = coroutine.resume(co, 5)
>>   print('E: errorfree, value, status', errorfree, value, coroutine.status(co))
>> end
----    1
A: odd  1
E: errorfree, value, status     true    1       suspended
----    2
B: odd  1
C: even 2
E: errorfree, value, status     true    -1      suspended
----    3
A: odd  3
E: errorfree, value, status     true    3       suspended
----    4
B: odd  3
C: even 4
D: even         4
A: odd  5
E: errorfree, value, status     true    5       suspended
----    5
B: odd  5
E: errorfree, value, status     true    nil     dead
>

我们有个for循环,它调用到两个函数:如果它是个奇数,就调用odd();是偶数,则调用even()。它的输出可能有点难看,所以我们就研究下由count计数的外部循环。已经加上了注释。

----    1
A: odd  1       -- yield from odd()
E: errorfree, value, status     true    1       suspended

在循环中,我们使用coroutine.resume(co,5)来调用这个协程。第一次调用是在进入协程函数的for循环中。注意下这个odd函数,它由我们协程函数中的yield调用。协程函数中不一定非得yield,这点很重要。使用yield,我们返回1。

----    2
B: odd  1       -- resume in odd with the values we left on the yield
C: even 2       -- call even and exit prematurely
E: errorfree, value, status     true    -1      suspended  -- yield in for loop

在第二个循环中,主循环yield并暂停了这个协程。这里的要点就是,我们可以在任何位置执行yield。我们不必纠结在协程中的一点执行yield。使用yield,我们返回-1。

----    3
A: odd  3       -- odd() yields again after resuming in for loop
E: errorfree, value, status     true    3       suspended
We resume the coroutine in the for loop and when odd() is called it yields again.

在for循环中,我们resume这个协程,在调用odd()时,它就再执行次yield。

----    4
B: odd  3       -- resume in odd(), variable values retained
C: even 4       -- even called()
D: even 4       -- no return in even() this time
A: odd  5       -- odd() called and a yield
E: errorfree, value, status     true    5       suspended

在第四个循环中,我们在离开时resume了odd()。注意下其中的变量都保留了,odd()函数的上下文在协程暂停时依然保留。even()函数执行到最后,我们到达了它的末尾。若使用coroutine.yield()以外的其他方式退出函数,函数的上下文及变量一律被销毁。只有使用yield才可以返回。

----    5
B: odd  5       -- odd called again
E: errorfree, value, status     true    nil     dead  -- for loop terminates
>

再次回到odd()。这次主循环到了5,也就是协程的极限。5以及for循环的状态在协程的整个执行过程中都有保留。每个协程都有自己的栈和状态,而我们一旦退出协程函数,它就销毁了。

本杰明巴顿奇事 1-3

作者:弗·司各特·菲茨杰拉德 Fitzgerald,F.S.
翻译:ssword

note:第一次尝试翻译文学的东西,自知英文中文水平都很不够,况且是译菲茨杰拉德先生的作品,心中很是不安。不过既然已经开始,那就试着写下去吧。一共十节,这是前三节。仅供学习交流,不当之处欢迎提出 :)

1 | 2 | 3


1

现在,小孩在医院里发出第一声啼哭已再寻常不过了。不过在1860年,在家里接生还是理所当然。所以那年夏天,年轻的罗格巴顿夫妇决定让他们第一个小孩在医院出生的时候,已经超前那个时代了五十年。

我只管告诉你后来发生的事情,信不信由你。

罗格巴顿一家在内战前的Boltimore商界和社交圈都有着显赫的地位,他们与每一个名门望族都有往来,其中甚至不乏国会的大员。第一次体验这古老的魔咒—生小孩—巴顿先生自然是非常的紧张。他希望能生个儿子,这一来就可以把他送到耶鲁。当年巴顿先生在那儿可是被人叫了四年的“天才”呢。

九月的大日子那天,巴顿先生起了个大早。匆忙整理了下衣着便大步朝医院跑去,希望能尽快亲眼见证这诞生于黑夜的生灵。

距离Maryland私立贵族医院还差不多100码的地方,他看到他的家庭医师科恩大夫正从台阶上下来,不停地搓着双手,像洗手一样—医生的职业病。

罗格巴顿先生,Roger Button & Co. 零售集团的老板,顾不得那个时代独有的绅士风度,慌张地朝他跑去。“科恩大夫!”他喊道,“啊,科恩大夫!”

大夫听到了他的声音,转过身来一动不动。瞧着巴顿先生越来越近,那职业的面部表情则是愈发的古怪。

“怎么样了?”巴顿先生还喘着粗气,“它在哪儿?她还好?儿子么?是哪个?什么…?”

“说清楚点!”科恩大夫像是被啥惹着了。

“孩子出生了吧?”巴顿先生恳求道。

科恩大夫迟疑了下。“呃,是的,是这样—算是吧”然后怪怪地瞥了巴顿先生一眼。

“我夫人还好?”

“还好。”

“男孩还是女孩?”

“够了!”科恩大夫恼了。“你还是自己看去吧,不可思议!”—最后这个词几乎是一口蹦出来的—又回头喃喃道,“你想象得出这样对我有啥好处!再多一个就会毁了我,毁了任何人!”

“怎么了?”巴顿先生有点摸不着头脑。“…三胞胎?”

“不,不是三胞胎!”大夫打断说,“你可以自己看去,还有,换个大夫。年轻人,是我把你带到这个世界上,我为你们家做了40年的医生。我受够了!我再也不要见到你和你家人!好自为之!”

他猛然转身,一言不发地钻进他停在路边的phaeton,飞一般地开走了。

留下巴顿先生愣在人行道上,浑身发抖。发生了什么可怕的意外?有这么一刻巴顿先生甚至不敢进医院大门了。迟疑了好一会儿,他才进了医院。

一个护士正坐在大厅的一角。咽下刚受的羞辱,巴顿先生向她走去。

“早上好。”她说,语气很和蔼。

“早上好,我…我是巴顿先生”

突然这女孩就变得惶恐不安。她站起来,像是想插根翅膀飞出去。

“我想见我小孩。”巴顿先生说。

护士没忍住叫出了生。“啊…当然!”她歇斯底里了。“上楼…右边…走—上楼!”

她指了方向。

巴顿先生打了个趔趄,带着一身冷汗上了二楼。在二楼的大厅遇见一个端脸盆路过的护士。“我是巴顿先生”,他强作镇定,“我想见我…”

哐当!

脸盆摔到了地上。

哐当!哐当!

脸盆顺着楼梯往下滚。

“我想见我小孩!!”巴顿先生几乎咆哮道,他快崩溃了。

哐当!脸盘滚到了一楼。护士恢复了镇定,轻蔑地瞟了巴顿先生一眼。

“好吧,巴顿先生,”她的语气很复杂,“好吧!但你得了解我们的处境!太不可思议了!医院要名声扫地…”

“快点!”他喊哑了,“我受够了!”

“这里走,巴顿先生。”

他跟她穿过大厅,到达一间屋子,里面各种哭号声交响不绝—实际上后来的人们正把它称作“哭屋”—他们进去了。

“呃,”巴顿先生有点接不上气,“哪个是我的?”

“那个!”护士说。

顺着护士所指的方向,巴顿先生看见一个约莫七十来岁的老头,裹着一条宽大的毛毯,挤在小婴儿床里。他那稀疏的头发差不多全白,下巴上还带着一撮滑稽的小胡子,随着窗子吹进来的风前摇后摆。他抬起呆滞的眼睛,不解地望着巴顿先生。

“我疯了么?”巴顿先生恼羞成怒了,“你们医院开的该死玩笑?”

“这不像是我们开的玩笑,”护士反驳到,“而且我也不知道您是不是真的疯了—可他就是你儿子。”

巴顿先生额上满是冷汗。他闭上眼,又瞪开—没错,眼前这人有七十公分—七十公分的婴儿!脚挤出婴儿床的婴儿!

老头端详了他二人一会儿,突然以沙哑的嗓音发话:“你就是我父亲吧?”

巴顿先生和护士不寒而栗。

“如果是,”老头发牢骚了,“那最好能带我离开这地方—要不最起码也该叫他们换一个舒服的摇椅才对呀。”

“以上帝的名义—你哪儿来的?你是谁?”巴顿先生暴跳如雷。

“我也说不清我是谁,”还是那牢骚腔,“我才出生了几个小时嘛…不过显然我姓巴顿。”

“你扯谎!你是冒牌的!”

老头无精打采地转向护士,“就这样欢迎新生儿,”语气很虚弱,“为什么不告诉他他错了?”

“你错了,巴顿先生。”护士斩钉截铁地说,“这就是你儿子,你最好接受这现实。我们希望你能尽快带他回家—尽快—今天!”

“回家?”巴顿先生觉得难以置信。

“是的,我们不能留他这儿。真的不能,明白?”

“我很高兴,”老头又牢骚,“真是安静人呆的宝地,四处都是哭哭闹闹,别想让我睡个安稳觉。我管人要吃的—”他升到一个让人发怵的音调以示不满—“他们居然给了我一瓶牛奶!”

巴顿先生瘫倒在他儿子身边的椅子上,双手捂脸,陷入深深的恐惧之中。“我的天啊!人们会怎么说?我该怎么办?”

“你该把他带回家,”护士坚持道,“—马上!”

可怜的巴顿先生眼前浮现出这样的画面:他走咋熙熙攘攘的大街上,后面亦步亦趋地跟着一个老头,跟鬼影似的纠缠不去。

“不行,不行!”他呜咽道。

人们会停下来跟他说话,他又该怎么说?他就这样介绍这老头,“这是我儿子,今天早晨刚出生的。”之后,这老头会紧一下裹着的毛毯,继续不紧不慢地跟着他走。走过大街小巷,走过奴隶市场—有那么一刻巴顿先生甚至巴不得他儿子是个黑人—慢慢地走….到居民区…到家岂不得猴年马月!”

“来!自己起来!”护士命令道。

“看哪,”老头突然喊道,“如果你觉得我会裹着这毛毯回家,那你就是大错特错了。”

“小孩子都是裹在毛毯里。”

噼啪一声,老头不怀好意地举起一件白色的小襁褓。“瞧啊!”他颤悠悠地说,“他们就拿这个对我!”

“小孩子都穿这个。”护士面不改色。

“好吧,”老头说,“这个小孩两分钟内啥也不会穿。这毛毯真痒,他们最起码得铺个床单!”

“穿着!穿着!”巴顿先生手忙脚乱了。他回头问护士,“我该怎么办?”

“到外面给你儿子买件衣服。”

巴顿先生儿子的声音又传出来了,到大厅都能听见。“拐杖,父亲,我还要一根拐杖。”

砰一声,巴顿先生狠狠甩上了门。


2

“早上好,”巴顿先生不安地说。对面是Chesapeake百货公司的店员。“我要给我小孩买件衣服。”

“请问您孩子多大?”

“差不多六个小时吧。”巴顿先生没来得及多想。

“婴儿服都在后面。”

“呃,不是—我没说清—它—他是个大号的小孩—罕见的—异常的—-呃—大。”

“我们有最大号的婴儿服。”

“童装部在哪儿?”巴顿先生惴惴不安地踱来踱去,他怀疑这店员一定是察觉到他那不可告人的秘密了。

“右边。”

“好的…”他犹豫了一想到儿子穿成人装的样子,他就难受的要命。要不然,他可以找个大号的童装,再给他剪掉那可怕的小胡子,染掉白发,好好打扮一番,兴许还可以挽留一些自尊—不过前提当然是,对谁也不能提!

但让人抓狂的是,童装部居然没有适合小巴顿的尺寸。

巴顿先生暗暗地诅咒这商店。像这种情形,换了谁都会诅咒的。

“请问您刚才说您小孩多大?”店员好奇地问。

“他…十六岁。”

“呃,请您原谅。我刚才听成了六个小时。青年部在下个过道处。”

失望的巴顿先生转身正要走,突然眼前一亮,指着展示窗的模特衣架大喊道“就那个!我要那件,模特上的那件!”

店员眼睛瞪得大大的。“为啥,”他抗议道,“这可不是童装…就算是吧,可也是化妆舞会上用的,你可以自己穿!”

“包起来,”他的顾客不安地说,“这就是我要的。”

惊愕的店员照做了。

回到婴儿室,巴顿先生径直把包扔给了他儿子。“你的衣服。”

老头打开包,不屑地瞧着里面的东西。

“瞧着挺有趣啊,”他抱怨道,“我可不想让人当猴…”

“你在拿我当猴耍!”巴顿先生恶狠狠地说,“不管穿上什么样子—穿上!不然—不然我打你屁股!”最后这句话可让巴顿先生噎的不行,感觉怪怪的,不过非说不可。

“好吧,父亲”—带着子女对长辈的那种敬意—“你活的久,知道的多。听你的。”

同刚才一样,这声“父亲”又让巴顿先生不寒而栗。

“抓紧。”

“我在抓紧,父亲。”

看着儿子穿戴完毕,巴顿先生是越发的沮丧。斑点袜,粉红裤,带白领的紧身短衫。不过上面还有个小胡子耷拉着,这效果可不大好!

“等等!”

巴顿先生顺手拿起一把剪刀,三下五除二给他剪了一大截。不过尽管如此,离完美还是差太远啦。还有那稀疏的白发,黯淡的眼睛和古黄的牙齿,与这一身打扮是多么的不搭调。巴顿先生—在审美上固执的很—朝他儿子伸手,厉声道:“过来!”

儿子信任地拉过父亲的手,“你打算怎么叫我,爸爸?”—在离开婴儿房的路上,他颤颤地说—“先叫着‘小孩’?想出好名字来再说?”

巴顿先生咳了声。“不知道,”他冷冷地说,“我想可能会叫你玛士萨拉。”

(译者注: 玛士萨拉,圣经旧约里长寿的人。)


3

尽管巴顿家的新成员已经理了发,刮了脸,头发也染成了怪怪的黑色,还从一个被惊呆了的裁缝那里定做了童装,巴顿先生依然无法无视这个事实—他儿子实在是古怪!除去驼背的因素,本杰明巴顿—最终给他取了这个名字,而没用那个带挖苦意味的“玛士萨拉”—可有五尺七英寸那么高—童装掩盖不了身高,正如染过的眉毛藏不住下面呆滞的眼睛。实际上,之前聘的育婴师来这儿这儿只看了一眼,就愤愤地不干了。

但巴顿先生坚持他的观点,本杰明是个小孩,小孩就该有小孩的样子。起初,他威胁说如果本杰明不喜欢喝牛奶,那就什么都不给他吃。不过到最后还是巴顿先生妥协了,允许儿子吃奶油面包,甚至燕麦片。一天,他带回来一个拨浪鼓,以为他儿子毫无疑问会“自己玩”。谁知本杰明压根就毫无兴致,一天下来也就是顺从地响两声好让他父亲高兴。

不玩拨浪鼓,本杰明自有更安静的方式来打发时间。一天,巴顿先生突然发现上周抽烟的数量大大增加,而这一现象很快就有了解释:他路过婴儿房,却发现满屋都是让人发晕的青烟,而本杰明则是一脸愧疚,想要藏起那只哈瓦的烟头。之后本杰明自然是挨了一通打屁股,不过巴顿先生也发现了自己对此无能为力,他就警告他儿子说抽烟会“妨碍他的成长”。

尽管如此,巴顿先生仍不放弃。他带回来玩具兵团,带回来小火车,带回来大大的毛绒动物,并且为了培养儿子的创造力,他还特意去询问玩具店员,如果“小孩吃了些颜料,会不会更容易画出小鸭子。”不过,不管父亲费了多大苦心,本杰明依然是无动于衷。他宁肯抱一卷大英百科全书,回婴儿房坐着看一下午,而把他的毛绒动物无视在地板上。显然,巴顿先生的努力收效甚微。

这件事情在baltimore可是轰动一时。还好突然爆发的内战及时转移了人们的注意力,要不然谁也无法估量这会对巴顿家族的社交产生多大影响。一些人挖空心思想要恭维巴顿夫妇,到最后却无一幸免没能避开本杰明这个敏感话题:他长得像他爷爷–就像七十岁的老头那样老!–毫无疑问,这让巴顿先生很是不爽。而本杰明的爷爷无端被人扯出来说三道四,更是气的不轻。

离开医院的本杰明,对待生活随遇而安,倒也自得其乐。一天家里来了好几个小孩,他就活动了下僵硬的关节,陪他们玩了一下午弹弓。一不小心甚至还打碎了个橱窗的玻璃。巴顿先生偷偷看见这漂亮的一击,眼前一亮。

从此本杰明就例行公事地每天打碎些东西。不过他这么做仅仅是为了让父亲高兴,让他觉得他儿子是生性好斗。

待爷爷消了当初的气,本杰明与这老绅士一起相处的十分融洽。虽然年龄和阅历都是差距相当大,不过他俩依然可以像老朋友那样,一起坐好几个小时,不嫌烦地谈论当天发生的无聊事情。相对于父母,他更喜欢跟他爷爷在一起—他爸妈对他像是有点敬畏的意思—一面是对他蛮横,一面却不时称呼成“先生”。

与其他人一样,本杰明对自己身体和心智的早熟也是十分不解。为此他翻阅了大量医学杂志,却没找到任何类似的先例。在父亲的鼓动下,他开始尝试与同龄的小孩一起玩些轻松的游戏—足球的活动量太大,他怕他那一身老骨头折腾不起。

五岁那年,本杰明进了幼儿园。上艺术课,小朋友们贴彩纸,编彩图,做纸项链。可本杰明总是打瞌睡,做不到一半就着了。此举让那年轻的老师又气又怕,为了解脱,她向本杰明的父母告了一状,于是本杰明就回了家。巴顿先生对他的朋友解释说,幼儿园是嫌他太小。

到了十二岁,巴顿夫妇已经习惯了他们儿子。不过习惯难改,他们依然认为本杰明像其他同龄人一样,是个小孩—除非偶然发生的一些反常事件迫使他们直面现实。不过在十二岁生日的几周后,本杰明有了个大发现。他照着镜子,眼前这是真的吗?在这十二年里,在染液掩盖下的头发,由灰白慢慢变成了棕黄?脸上的沟壑,慢慢地变浅了?皮肤也更红润,更结实了?他说不清。不过可以明确,他已经不再驼背了,而且从出生开始,他的体格就一直在变好。

“会不会是…”他想,觉得难以置信。

他去见父亲。“我长大了,”他自豪地宣布,“我要穿长裤。”

“呃,”他父亲犹豫了下,最后说,“我不知道。十四岁才是穿长裤的年龄…你才十二。”

“但你得承认,”本杰明反驳道,“我比同龄人都要壮。”

父亲百思不得其解地望着他。“嗯,这可没准,”他说,“我十二岁时可跟你一般壮实。”

他撒了谎—这等于说,罗格巴顿终于默认了他儿子的与众不同。

最后他们达成了一项协议,本杰明继续染发,继续跟同龄的小孩一起玩,上街的时候不戴眼镜,不拄拐杖。作为回报,他穿上了第一条长裤…

vim quick reference card

原地址:http://tnerual.eriogerg.free.fr/vim.html
翻译:ssword

这里的排版弄的挺难看的,大家如果觉得有用,就将就下吧。

Basic movement
基本移动

h l k j 左移、右移一个字符;上一行,下一行;
b w 左移、右移一个词元
ge e 左移、右移到一个词元末尾
{ } 前、后移动一个段落
( ) 前、后移动一个句子
0 ^ $ 句子的开头、首字符、最后一个字符
nG ngg 第n行的句首、句尾
n% 文件的n%处(n不可省略)
n| 当前行的第n列
% 匹配下一个括号,中括号,大括号,注释或#define
nH nL 当前窗口从顶数、从底数第n行
M 当前窗口的中间行

Insertion & replace→ insert mode
插入&替换 ->插入模式

i a 在光标前、后插入
I A 在当前行前、后插入
gI 在当前行的第一列插入
o O 在当前行的上、下插一新行
rc 把光标下的字符替换为c
grc 同rc相似,不过不影响布局
R 替换从光标往后的所有字符
gR 同R相似,不过不影响布局
cm 修改到移动命令m指向的位置
cc or S 修改当前行
C 修改到行尾
s 修改一个字符,并进入插入模式
~ 转换当前字符的大小写,并右移光标
g~m 转换字符大小写到移动命令m指向的位置
gum gUm 将移动命令m中间的字符转换为大写、小写
m 按照移动命令m,左移、右移缩进
n< < n>> 左移、右移n个缩进

Deletion
删除

x X 删除当前字符、前一个字符
dm 删除到移动命令m指向的位置
dd D 删除当前行
J gJ 将当前行与下一行合并
:rd↵ 删除区间内的文本
:rdx↵ 删除区间内的文本,并将其存入寄存器x

Insert mode
插入模式

^Vc ^Vn 逐字插入字符c、数值n
^A 在当前输入的文本前插入
^@ 同^A,并退出到命令模式
^Rx ^R^Rx 逐字)插入x寄存器的内容
^N ^P 在光标前、光标后自动完成
^W 删除光标前一个单词
^U 删除前面输入的文本
^D ^T 左移、右移一个缩进
^Kc1c2 or c1←c2 插入一个digraph
^Oc 进入临时命令模式执行c
^X^E ^X^Y 向上、向下滚动
回到命令模式

Copying
复制

“x 将下个用于删除、复制、粘贴的寄存器设置为x
:reg↵ 显示所有寄存器的内容
:reg x↵ 显示x寄存器的内容
ym 复制移动命令m之间的文本
yy or Y 复制当前行
p P 将寄存器中的文本粘贴到光标前、后
]p [p 同p P,并自动处理缩进
gp gP 同p P,并保留光标。

Advanced insertion
高级插入

g?m 对移动命令m中间的文本进行rot13加密
n^A n^X 对移动命令m中间的文本进行rot13加密
gqm 将移动命令m中间的多行文本格式化为同一宽度
:rce w↵ 将区间r中的内容以w为宽度居中
:rle i↵ 将区间r中的内容以i个缩进左对齐
:rri w↵ 将区间r中的内容以w为宽度右对齐
!mc↵ 以c命令过滤处理移动命令m中间的文本
n!!c↵ 以c命令过滤处理n行文本
:r!c↵ 以c命令处理区间r中间的文本

Visual mode
可视模式

v V ^V 开始、退出选取文本
o 将光标移动到选取文本的开始
gv 回到上一个选取的文本
aw as ap 选取一个单词、句子、段落
ab aB 选取一个()、{}块

Undoing, repeating & registers
撤销,重复和寄存器

u U 撤销上一个命令,返回上一个修改的行
. ^R 重复上一个修改,重复上一个撤销
n. 重复上一个修改n次
qc qC 记录、追加输入的字符到寄存器c
q 停止记录
@c 执行寄存器c的内容
@@ 重复执行上一个@命令
:@c↵ 把寄存器c作为Ex命令执行
:rg/p/c↵ 对区间r中匹配p的文本执行Ex命令c

Complex movement
高级移动

- + 上移,下移到行首
B W 按空格左移、右移一个词元
gE E 按空格左移、右移到一个词元的末尾
n 下移n-1行到行首
g0 gm 当前行首、行中央
g^ g$ 当前行的首字符、尾字符
gk gj 上移、下移
fc Fc 下一个、前一个字符c
tc Tc 下一个、前一个字符c的前面
; , 重复上一个fFtT操作,反方向
[[ ]] 上一个、下一个节开头
[] ][ 上一个、下一个节结尾
[( ]) 上一个、下一个未关闭的括号
[{ ]} 上一个、下一个未关闭的大括号
[m ]m 上一个、下一个java方法的开头
[# ]# 上一个、下一个未关闭的#if #else #endif
[* ]* 上一个、下一个/* */的开头和结尾

Search & substitution
搜索 &替换

/s↵ ?s↵ 向前、向后搜索s
/s/o↵ ?s?o↵ 按o个偏移向前、向后搜索s
n or /↵ 向前重复上一个搜索
N or ?↵ 向后重复上一个搜索
# * 向前、向后搜索当前词元
g# g* 同上,额外匹配不完整的词元
gd gD 当前符号的局部、全局定义
:rs/f/t/x↵ 将区间r中匹配f的文本替换为t
:rs x↵ 以新的区间和x重复替换

Special characters in search patterns
模式匹配中的特殊字符

. ^ $ 任一字符,行的首字符,尾字符
\< \> 单词的开头,结尾
[c1-c2] 在c1..c2之间的任一字符
[^c1-c2] 不在c1..c2之间的任一字符
\i \k \I \K 标志符,关键字;字母,数字
\f \p \F \P 文件名;可打印字符;字母;数字
\s \S 空格;非空字符
\e \t \r \b ←>键, , < ?>, < ←>
\= * \+ 匹配0个或一个、0个或多个、一个或多个模式
\| 两个选择
\( \) 将一组模式组合成一个
\& \n 匹配全部、匹配第n个括号中的内容 *
\u \l 匹配下一个大写、小写字母
\c \C 忽略、匹配下一个模式

Offsets in search commands
偏移

n or +n 下n行的第1列
-n 上n行的第1列
e+n e-n 匹配文本结尾右边、左边的第n个字符
s+n s-n 匹配文本右边开头右边、左边的第n个字符
;sc 向下执行搜索命令sc

Marks and motions
标记 &跳转

mc,c∈[a..Z] 把当前位置标记为c,c∈[a..Z]
`c `C 跳到当前文件、任意文件的c标记
`0..9 跳到上一个位置
“ `” 跳到上一个位置,上一次编辑的位置
`[ `] 跳到上一个修改段落的开头、结尾
:marks? 输出可用的标记列表
:jumps? 输出跳转列表
n^O 跳到跳转列表的前一个位置
n^I 跳到跳转列表的后一个位置

Key mapping & abbreviations
键映射 &缩写

:map c e↵ 在普通模式和可见模式中将c映射为e
:map! c e↵ 在插入模式和命令模式中将c映射为e
:unmap c↵ :unmap! c↵ 移除映射c
:mk f↵ 将当前的映射和设置写入到文件f
:ab c e↵ 把e设置为c的别名
:ab c↵ 显示c开头的所有别名
:una c↵ 移除别名c

Tags
标签

:ta t↵ 跳到t匹配的tag
:nta↵ 跳到列表中后面第n个tag
^] ^T 跳到光标指向的tag,从tag返回
:ts t↵ 列出匹配的tag供选择跳转
:tj t↵ 跳到标签t,如果有多个匹配则提示选择
:tags↵ 显示tag列表
:npo↵ :n^T↵ 向前返回、跳至第n个tag
:tl↵ 跳到最后一个匹配的tag
^W} :pt t↵ 当前光标指向的前一个tag、 t匹配的tag
^W] 分割窗口,显示当前光标指向的tag
^Wz or :pc↵ 关闭显示tag的窗口

Scrolling & multi-windowing
滚动 &多窗口

^E ^Y 向上、向下滚动一行
^D ^U 向上、向下滚动半页
^F ^B 向上、向下滚动一页
zt or z↵ 将当前行滚动到窗口顶部
zz or z. 将当前行滚动到窗口中央
zb or z- 将当前行滚动到窗口底部
zh zl 向右、向左滚动一个字符
zH zL 向右、向左移动半屏
^Ws or :split↵ 将窗口分割成两个
^Wn or :new↵ 创建一个新窗口
^Wo or :on↵ 关掉当前窗口以外的其他窗口
^Wj ^Wk 移动到下一个、上一个窗口
^Ww ^W^W 移动到下一个、上一个窗口 (wrap)*

Ex commands
Ex命令

:e f 编辑文件f,如果没有更改
:e! f 强制编辑文件(默认覆盖原先的修改)
:wn :wN 写入文件,并编辑下一个、前一个文件
:n :N 编辑文件列表中的下一个、前一个文件
:rw 把区间r写入到当前文件
:rw f 把区间r写入到文件f
:rw>>f 把如见r追加到文件f
:q :q! 退出并确认,强制退出忽略修改
:wq or :x or ZZ 写入到当前文件并退出
:r f 将光标下的内容插入文件f
:r! c 将光标下的内容经命令c处理的结果插入
:args 显示参数列表
:rc a :rm a 复制、移动区间r到a行下

Ex ranges
Ex区间

. $ 文件的当前行,最后一行
% * 整个文件、可视部分
‘t 标记t指向的位置
/p/ ?p? 匹配的下一个、前一个位置
+n -n 当前行的前n-1行、后n-1行

Folding
折叠

zfm 创建折叠到移动命令m指向的位置
:rfo 为区间r创建折叠
zd zE 移除当前折叠、当前窗口的所有折叠
zo zc zO zC 打开、关闭一个折叠;递归地执行
[z ]z 移动到当前打开折叠的开头、结尾
zj zk 向下、向上移动到下一个折叠的开头、结尾

Miscellaneous

:sh↵ :!c↵ 运行shell,执行shell命令c
K 在man中搜索当前关键字
:make↵ 执行make,读取错误并跳转到首个错误
:cn↵ :cp↵ 显示下一个、前一个错误
:cl↵ :cf↵ 显示所有错误、从文件中读取错误
^L ^G 重绘屏幕,显示文件名及位置
g^G 显示光标所在行、列及字符位置
ga 显示当前字符的ascii值
gf 打开当前光标下的文件名
:redir>f↵ 将输出重定向到文件f
:mkview ↵ 保存view配置[到文件f]
:loadview ↵ 装载view配置[从文件f]

ps:高数59分华丽地挂掉鸟~