带条件的前置路径
该功能属于订阅功能,用户需要购买岁寒输入法的订阅服务方可使用此项功能。
带条件的前置路径的实现,从某种意义上,促使岁寒输入法进化为一个平台化的输入法,一个拼音输入方案的超集。
自岁寒拼音滑行输入方案问世以来,我对外的宣传口径一直都是“岁寒是双拼输入法的变种”,换言之,即岁寒是双拼的一个子集,他们的关系类似下图。
虽然有人建议我放弃这种说法,把岁寒宣传成全拼输入法的一种,如此更符合使用上的体验,也不会让新用户感觉过于陌生,滋生畏难情绪。毕竟岁寒确实与一般意义上的双拼有很大的不同,因此上述示意图并不能恰如其分地描述岁寒和双拼之间的关系,至少不应该是全包含的关系。但我自问,在设计岁寒输入法之初,更多从双拼输入法中汲取到了思想上的营养,所以我始终将岁寒视作双拼的一种,大体而言将岁寒视作双拼的一个子集,并无问题。
众所不知的是,双拼是一个相对小众的输入方案,很多人可能连双拼这个名词都不曾听说。而岁寒作为双拼的一个子集,则更是小众的小众,这严重限制了岁寒输入法潜在受众的基数。如何扩大岁寒的受众面也成为一直困扰我的重点难题。然而盲目地扩张功能以迎合更多用户需求的做法一直都是我极力反对的,所以我不曾听从那些让我加入全拼和九宫格方案之类的建议,我始终在极力避免岁寒输入法变得四不像,也在避免他泯然如众人。我相信一个输入法的发展要秉承一些贯彻始终的理念,具体的功能反而是次要的,但众多功能背后要传达的理念才是作品的根本。所以我始终在思考我要坚持的理念究竟为何?
从岁寒输入法的发展脉络来看,岁寒输入法1是滑行+图形识别+五笔,岁寒输入法2是滑行+笔画+五笔,岁寒输入法3以后的版本是滑行+双拼。可以看出的是,我想要坚持的并不是笔画、不是五笔,也不是双拼,不是某个具体的输入方案,而是滑行输入这件事情,我真正想要探问的,是滑行输入这个新维度究竟能给中文输入法世界带来多少及多深的改变?岁寒拼音的基本方案也好,韵母键族也好,前置路径也好,都是在为滑行输入这个目的服务。所以我也一直在思索,还有什么办法可以让岁寒在滑行输入这件事上做得更好,变得更强。
前置路径
随着我思考的加深,我把目光放到了前置路径身上。在设计前置路径之初,我对这个工具抱有巨大的期望。前置路径是对岁寒输入法已有的方案的继承和颠覆,因为他赋予了用户创造一套全新的滑行输入方案的能力。然而前置路径推出后,实际效果并没有我所期待的好。首先,是只有极少数几名岁寒输入法爱好者会去开发前置路径的潜能;其次,是他们所开发出来的新的滑行方案其实用者寥寥,很多使用者可能只是浅尝辄止,随后便将这些新方案束之高阁。是这些方案设计得很不好吗?也不尽然,爱好者们花费心思鼓捣出来的方案必然浸润他们的细腻心思在其中,一定有其出彩之处。但现实是,但凡有点学习成本,就足以将大多数人拒之门外。我需要为他们寻找破局的钥匙。我发现一个情理之中的现象,大多数基于前置路径开发出来的新滑行输入方案都是对传统双拼输入方案的复刻。遗憾的是,当前的前置路径并不能完美地复刻传统的双拼输入方案,这迫使设计者不得不采取折衷的做法,结果就是最终被设计出来的方案变成了一个强加上滑行效果的双拼怪胎方案,即便是原双拼输入方案的使用者也不一定能对之提起兴致。所以并不是岁寒输入法的爱好者们不够有创新精神,终究还是前置路径这个功能还不够强。在注意到这一点后,我问自己,既然爱好者如此希望复刻其他双拼方案,那我何不干脆就赋予他们这种能力?甚至是超越这种能力的东西。
在介绍我如何改进前置路径之前,读者需要了解岁寒输入法中的滑行路径表示法和前置路径要如何使用和配置,请参考以下章节:
双拼简介
在有了上述背景知识之后,我需要简单介绍一下双拼的背景知识。传统的双拼方案的输入思路是基于分时复用键盘键位实现的,我们可以简单的认为奇数次击键输入为声母,而偶数次击键输入为韵母。但分时复用其实不够,因为在双拼体系下,汉语拼音会被简单地拆分成声母和韵母两部分,声母的数量是23个,韵母的数量则是33个。众所周知,英文只有26个字母,我们当然可以轻易地将23个声母分配到26个键位上,但我们又该如何将33个韵母分配到26个键位上的呢?这不是还差7个键位吗?有的双拼方案会把;号键也利用上,但那也差了6个键位。把更多的非字母键位利用起来自然能解决问题,但显然这个解决方案并不优雅,实际上大多数双拼输入法也没有这么做的。那他们是怎么做的?这就要利用汉语拼音的另一个特点了——声母和韵母并不是可以任意组合的。也就是说,某些声母和某些韵母的组合在汉语拼音下是非法的,比如giao这个拼音就是没有的,虽然我们可以发这个音,但汉语里并没有与之对应的汉字,所以这个拼音是非法的。诸如此类的拼音还有很多,有些韵母之间甚至是互斥的,它们没有相同的可与之组合的声母。利用这一点,我们就可以让一部分存在互斥关系的韵母共享一个键位,从而将33个韵母分配到26个甚至更少的键位上。当我们点击一个存在韵母共享的键位时,输入法需要根据已经输入的声母来判断这个键位的实际释义。只要前置路径能够将上述的过程模拟出来,前置路径功能就具备完美复刻任何一款双拼输入法的能力。看到这里,不知道你是否已经猜到了我想做什么——我要给前置路径功能加上条件判断的能力。
带条件的前置路径
前置路径表示法
我将此前的前置路径的形式称为无条件的前置路径;
下面引入一个扩展后的表达形式,称为带条件的前置路径。
与此前的前置路径格式的区别就是在左边加上条件和:,并且在带条件时,要求最右边的输入部分可以是声母、韵母、韵母的前缀,或者是由声母和韵母组成的拼音。
条件的组成与要求
条件部分可以由以下要素组成:
任一声母
b p m f d t n l ɡ k h j q x zh ch sh r z c s y w
和零声母(用数字0
表示,关于零声母下文会详细讲解)解释:当条件声明为声母时,表示当前输入位置上存在且仅存在所声明的声母时,将指定路径接受为指定的声母或韵母,并输入到当前的输入位置上;示例如:
s:a=ang //该式表示当当前输入位置为s时,点击a, //在当前输入位置上输入ang,当前输入位置将变为sang;
(该条件的解释在拼音替入时存在例外,在优先级一节中介绍)
任一韵母或任一韵母的前缀;韵母的数量有点多,这里就不一一列举了。那什么是韵母的前缀呢?韵母的前缀就是由合法韵母从左到右若干个字母组成的字符串,即便不是合法的韵母也可以,如:iong的前缀:io、ion;
解释:当条件声明为韵母或韵母的前缀时,表示当前输入位置上存在且仅存在所声明的韵母或韵母的前缀时,将指定路径接受为指定的声母或韵母,并输入到当前的输入位置上;示例如:
a:n=an //该式表示当当前输入位置为a时,点击n, //在当前输入位置上输入an,当前输入位置将变为an;
下划线+任一韵母或下划线+任一韵母的前缀,以an为例,则形如
_an
;这里下划线表示韵母的左侧有声母(这里的声母包括零声母);解释:当条件声明为下划线+任一韵母或下划线+任一韵母的前缀时,表示当前输入位置上存在所声明的韵母或韵母的前缀,且存在任一声母,如所指定的输入为声母时,则将指定路径接受为该声母,并输入到当前的输入位置上;如所指定的输入为韵母且该韵母能与已存在的声母匹配时,则将指定路径接受为该韵母,并输入到当前的输入位置上;示例如:
_a:n=an //该式表示当当前输入位置为形如xa的输入时, //其中x表示任一声母,比如sa,点击n, //由于an能与s匹配,因此在当前输入位置上输入an, //当前输入位置将变为san;
优先级
前置路径之所以被我命名为前置路径,就是因为前置路径的优先级高于键位自带的键义,即当前置路径被触发时会覆盖键位本身的输入; 那现在前置路径有了无条件和带条件之分,为了避免歧义,需要对他们的优先级明确:带条件的前置路径优先于无条件的前置路径。即当带条件的前置路径和无条件的前置路径都被满足时,带条件的前置路径被触发,无条件前置路径不被触发;之所以如此规定,是因为如果无条件的前置路径优先级更高的话,则带条件的前置路径将无被触发的可能。 在拼音替入操作中,允许声母条件在有韵母时被触发,这是声母条件的例外情况。因此声母条件和下划线+韵母条件就可能存在冲突。 当带条件的前置路径中条件存在冲突时,下划线+韵母条件优于声母条件,即当下划线+韵母条件和声母条件同时被满足时,下划线+韵母条件被触发,声母条件不被触发。下划线+韵母条件和韵母条件在理论上不会发生冲突,因此他们是平级的。 优先级总结: 下划线+韵母条件前置路径=韵母条件前置路径>声母条件前置路径>无条件前置路径>键位自带键义
语法糖
条件的简写
为了便于描述多个条件下的同一路径的同一输入,允许在一条表达式中,用/分隔多个条件,如:
s/c/z/sh/ch/zh:a=ang //该式表示当当前输入位置为s、c、z、sh、ch或zh时,点击a, //在当前输入位置上输入ang;
反选声母的写法
当声母条件较多时,为了缩短条件列表,允许在需要排除的声母条件最前面添加~,表示选择所列出来的声母之外的其他所有声母作为条件,如:
~s/c/z/sh/ch/zh:a=ang //该式表示当当前输入位置为s、c、z、sh、ch或zh之外的其他声母时,点击a, //在当前输入位置上输入ang;
最极端的例子是全选所有声母:
~:a=ang //该式表示当当前输入位置存在声母时,点击a, //在当前输入位置上输入ang;
注意:反选写法仅适用于反选声母,不适用于反选韵母,列在反选条件列表的韵母条件将被忽略;换言之,韵母条件只能使用正选写法;
*语法
为了方便地声明与某个韵母匹配的所有声母(除零声母外),可以使用*加韵母的形式来声明与该韵母匹配的所有声母(除零声母外)作为条件,如:
*ang:a=ang //该式等同于b/c/d/f/g/h/k/l/n/m/p/r/s/t/w/y/z/sh/ch/zh:a=ang //或~j/q/x/0:a=ang
需要强调的是,使用此方式声明的声母中是不包含零声母的; 如果需要包含零声母,则可以写成如下形式:
*ang/0:a=ang //或 0/*ang:a=ang
上述语法将对自定义双拼方案带来巨大的便利;
!语法
这个语法作用正好与前面的*语法相反,是用于筛选出与指定韵母不匹配的所有声母(除零声母外),形式为!加韵母,如:
!ang:a=ang //该式等同于j/q/x:a=ang
需要强调的是,使用此方式声明的声母中是不包含零声母的; 如果需要包含零声母,则可以写成如下形式:
!ang/0:a=ang //或 0/!ang:a=ang
总结
至此关于带条件的前置路径的使用方法已经介绍完了,接下来两节我们来实践一下,看看如何利用带条件的前置路径实现自定义的双拼方案和全拼方案吧。