拼写运算详解
2012-02-14
拼写运算
「重现世间无匹好兵器,一发招覆雨惊天地。」
Rime输入法独门绝活之「拼写运算」,是按规则改写编码的形式、实现多种定制功能的技术。
概念
先来介绍本文所使用的术语:
- 输入法 : 以输入信号的序列到输出文本序列的转换方法;Rime可搭载多种不同类型的输入法,称其为「输入方案」
- 字符集 : 构成目标输出文本的字符的集合,主要由汉字组成
- 编码 : 用于检索目标字符的字母序列
- 字母 : 构成编码的字符,亦称「码元」
- 字母表 : 字母的集合,亦称「编码字符集」,通常由小写拉丁字母组成
- 码表 : 目标字符集与编码集合之间的映射表
- 码长 : 单个编码所包含的字母个数;码表中所有编码码长一致,本文称其为「定长编码」;规定了最大码长的编码方案,本文称其为「限长编码」
- 编码空间 : 给定的字母表以有限的码长排列组合所得的可用编码数目
- 音节表 : 输入方案中所有编码的集合;拼音输入方案中,彼此不同的音节是可穷举的,其音节表是个固定的集合;多数形码输入法按照一定规则在给定的编码空间内为新生词组编码,故无法给出固定的音节表
- 重码 : 对应到多个输出字符的编码
- 输入码 : 用作输入的字符序列
- 候选 : 与输入码相关联的目标输出文字;当有重码时,候选文字形成一个列表可供用户挑选
- 拼写 : 与单个编码相对应的输入码;拼写可能不同于编码
- 拼写法 : 一种输入方案里,有效拼写的集合到编码集合的映射,亦称「正字法」
- 拼写运算 : 以拼写为运算元的一元运算,通过字符串匹配及替换操作对拼写实施文字变换,获得一个新的拼写,并可赋予结果附加的属性
- 投影 : 以拼写法为运算元的一元运算,获得一个衍生的拼写法;实际运用时,通常对拼写法连续执行一组投影操作;每一轮操作中,对拼写法里的每个有效拼写做一次拼写运算,从而获得新的有效拼写集合,并重新建立其与编码集合的映射
功能
传统的中文输入法是基于码表的,通过键盘输入特定字母序列,获得与该编码对应的文字。 而大多数编码方案,都不会用尽编码空间内的所有编码;与字符集内的文字无对应的编码,就在编码空间中形成空位。 为了提高输入效率,输入法会利用编码空间中的空位,编制简码、容错码等衍生编码,以在尽量多的场合为用户提供有意义的候选文字。
在某些输入方案里,简码、容错码可以从源码表按照一定规则产生。 如:汉语拼音输入法中的简拼,取拼音音节的首字母,也可取以双字母表示的声母zh、ch、sh。 这是因为编码方案所遵循的语言学原理,决定了拼音音节在编码空间内的分布及留下的空位,呈某种规则的形式。 故,产生简码、容错码的规则是与输入方案相关的,一款通用的输入法软件要以与编码方案相适应的方式生成这些衍生编码。
拼写运算,提供了描述产生式规则的能力!
为了提供多样化的输入体验,Rime可借由一份码表制作多款输入方案。 譬如:注音符号、汉语拼音这两套注音方案是为相同音系设计的(忽略地区间字音标准的差异)。 今有一份注音码表,按一定对应规则将注音转换为汉语拼音,可使注音、汉语拼音,甚至还有基于汉语拼音的双拼等输入方案复用同一部词典。
拼写运算,提供为输入方案重构拼写法的能力!
此外,改变编码回显的样式、按特定规则生成词组编码……拼写运算也可用来实现此等需要定制能力的算法。
原理与实现
拼写运算,借助正则表达式实现其字符串处理能力。 进一步,利用数学知识,构造出建立在输入法编码集合上的代数系统。
拼写运算实现为Rime程序库中的一套算法,可从Rime配置文件导入一组算式,执行规定的计算步骤。 运算步骤以YAML字符串列表的形式定义;每个列表项为描述一项运算的算式。 算式中包含的正则表达式,遵照Perl正则表达式的语法规范。
拼写运算的算式
格式为:<运算子><分隔符><参数1><分隔符><参数2><分隔符>...
分隔符为单个ASCII字符,通常用符号或空白字符。
举例:
xlit/abc/ABC/
上式中,运算子为 xlit(转写),分隔符为“/”,有两个参数 "abc"、"ABC"
如果参数中不包含空格,也可写作:
xlit abc ABC
注意:作为分隔符的字符不能在参数中出现;不同于Perl的 s/\//\\/ 语法:拼写运算式不支持在参数中将用作分隔符的字符用“\”转义表示。
拼写运算的运算子
* 转写/Transliteration : 依次将拼写中见于<左字母表>的字符替换为<右字母表>对应位置的字符。左、右字母表应包含相同数目的Unicode字符。
格式:xlit/<左字母表>/<右字母表>/
实例:算式 xlit/abc/ABC/ 运算元 abracadabra 结果 ABrACAdABrA
* 变形/Transformation : 若拼写(或其子串)与<模式>匹配,则将所匹配的部份改写为<替换式>;否则拼写保持不变。模式、替换式遵循Perl正则表达式语法。
格式:xform/<模式>/<替换式>/
实例:算式 xform/^([nl])ue$/$1ve/ 运算元 nue 结果 nve
效果:输入nve(lve)可以获得源码表中与编码nue(lue)对应的候选;输入nue(lue)无候选
* 消除/Erasion : 若拼写与<模式> 完 全 匹配,则将该拼写从有效拼写集合中消除。
格式:erase/<模式>/
实例:算式 erase/^.*\d$/ 运算元 dang1 结果 带声调的拼音不再可用
* 派生/Derivation : 若对拼写做正则模式匹配、替换而获得了新的拼写,则有效拼写集合同时包含派生前后的拼写;否则仅保留原拼写。
格式:derive/<模式>/<替换式>/
实例一:算式 derive/^([nl])ue$/$1ve/ 运算元 nue 结果 nve
效果:输入nve或nue(lve或lue)均可获得源码表中与编码nue(lue)对应的候选
实例二:算式 derive/^[nl](.*)$/l$1/ 运算元 na 结果 la
效果:输入la可获得源码表中与编码na、la对应的候选;输入na,候选仍为码表中编码为na的候选
* 模糊/Fuzzing : 执行派生运算;派生出的拼写将获得「模糊」属性,可设定将其用作构成词组的简码、但不用于输入单字。
格式:fuzz/<模式>/<替换式>/
实例:算式 fuzz/^([a-z]).+([a-z])$/$1$2/
效果:以首、尾码为多字母音节码的构词码。
注:需配合 script_translator 的选项 `translator/strict_spelling: true` 方可限定该拼写不用于输入单字。
* 缩略/Abbreviation : 执行派生运算;派生出的拼写将获得「缩略」属性,会在音节切分时与通常的拼写做区分处理。
格式:abbrev/<模式>/<替换式>/
实例:算式 abbrev/^([a-z]).+$/$1/
效果:以首字母为多字母音节码的缩写。
注解:
「转写」是拼写运算中目前唯一一则将运算元和参数作UTF-32编码、而非UTF-8编码处理的运算。 意味着,字母表可以采用ASCII范围以外的字符、字母表的长度按照Unicode字符数来计算。
「转写」和「变形」两则运算,除在拼写法投影操作中起重要作用,还可用于对单个字符串进行变换。 「消除」、「派生」和「缩略」,用于定义拼写法投影中非一一映射的情况。
「消除」就给定的模式,对运算元做完全匹配,即regex match操作; 「变形」、「派生」和「缩略」则可做部份匹配,相当于regex search/global replace操作。
投影算法
在拼写法投影操作P[x,y,z]里,每项运算x, y, z作为投影的一个步骤,依次从作为运算元的拼写法中产生一套新的拼写法; 将拼写法投影用于构建拼写-编码映射时,用户的输入是随意的;而码表中,音节表是固定的集合A。
所以Rime选音节表A上的初始拼写法(A -> A)为投影的运算元,逐步推导出映射到音节表A的有效拼写集合B,即所求的拼写法(B -> A)。
算法:
记音节表为A,拼写运算为序列[x,y,z],该投影的结果记为 P[x,y,z](A -> A)
Sa = { a -> a | for a in A } = (A -> A)
Sx = P<x>(Sa) = { x(a) -> a | for (a -> a) in (A -> A) } = (B -> A)
Sy = P<y>(Sx) = { y(b) -> a | for (b -> a) in (B -> A) } = (C -> A)
Sz = P<z>(Sy) = { z(c) -> a | for (c -> a) in (C -> A) } = (D -> A)
P[x,y,z](Sa) = Sz
在Rime输入方案中的用法
一例:仓颉输入方案(cangjie5.schema.yaml),在编码区回显仓颉字母
translator:
preedit_format:
- xlit|abcdefghijklmnopqrstuvwxyz|日月金木水火土竹戈十大中一弓人心手口尸廿山女田难卜符|
一例:朙月拼音(luna_pinyin.schema.yaml),显示拼音字母“ü”
translator:
preedit_format:
- xform/([nl])v/$1ü/
这一处,拼写运算的作用对象是编码回显区的拼音串,串中可能包含多个拼音音节,并已经自动插入了隔音符号。 为了替换该拼音段中所有匹配的字母,模式中并未用锚点匹配音节的头尾位置。
一例:朙月拼音(luna_pinyin.schema.yaml),定义简拼、容错拼写。
speller:
algebra:
- abbrev/^([a-z]).+$/$1/ # 简拼(首字母)
- abbrev/^([zcs]h).+$/$1/ # 简拼(zh, ch, sh)
- derive/^([nl])ve$/$1ue/ # 设 nue = nve, lue = lve
- derive/ui$/uei/ # 设 guei = gui,...
- derive/iu$/iou/ # 设 jiou = jiu,...
- derive/([aeiou])ng$/$1gn/ # 容错 dagn = dang,...
- derive/ong$/on/ # 容错 zhonguo = zhong guo
- derive/ao$/oa/ # 容错 hoa = hao,...
- derive/([iu])a(o|ng?)$/a$1$2/ # 容错 tain = tian,...
编译输入方案时,将运用这组运算规则完成音节表上的投影,求得可解析为音节代码的有效拼写集合; 输入过程中,这组有效拼写决定着输入码的音节切分方式。
一例:在拼音输入法中定义模糊音 zh=z, ch=c, sh=s, n=l, en=eng, in=ing
speller:
algebra:
- derive/^([zcs])h/$1/
- derive/^([zcs])([^h])/$1h$2/
- derive/^n/l/
- derive/^l/n/
- derive/([ei])n$/$1ng/
- derive/([ei])ng$/$1n/
# 模糊音定义先于简拼定义,可令简拼支持以上模糊音
- abbrev/^([a-z]).+$/$1/
- abbrev/^([zcs]h).+$/$1/
工具
除Rime输入法主程序外,拼写运算还用于:
- 输入方案部署工具:将投影所得的拼写法制成Prism文件,供Rime输入法于工作时快速访问
- 拼写运算调试器(已停止开发):创作输入方案时,用此工具调试算式、验证运算结果
讨论
拼写运算技术及应用技巧,你有好的话题,请在本页或 Rime 官网讨论页 留言。