Skip to content

一点哲学探讨

DANGER

本页面充满了个人主观意见。请批判性阅读。

本章预设读者了解至少一种形码。

You could have invented Moran——魔然设计的逻辑

魔然方案可以看作是沿着「智能拼音挂接」路子走到尽头的产物。具体来说,整个设计是从一个根本出发点开始,依逻辑不断推演得到的产物:

  • 想打词
  • 为了打词轻松,词语编码应该以音码为主
  • 为了词语码长短,以双拼为基础
  • 为了词语编码空间尽可能大(双拼离散较差),选择不定长编码
  • 为了不定长编码、兼容纯双拼、自动造词等功能,选择基于整句实现
  • 为了解决整句的不确定性(在开启了用户词库时),设置固定简码
  • 为了让简码的条反可以延续到整句,整句必须支持辅助码混输,并且整句用的辅助码必须和简码中用到的形码是一致的

(其实上面每一步都值得写一小节来解释我的理由,不过这里就从略了。)

因此,整个魔然方案是完整的一体,各模式其实并不能分开来看。(除了辅筛是例外,它可以干净利落地拆成别的方案。)

The Consistency Question——简繁字形的兼容性

虽然上面说得一套一套的,但是魔然最初的出发点并不是「我觉得市面上的方案都不适合我!我要做一个最强的输入方案!」,而是一个小小的想法:「我要实验一下有没有方案可以同时兼容简繁字形!」——当时,我还在使用形码。

这个想法最初来自于 2022 年笔者对小鹤音形的尝试,发现虽然小鹤音形的码表是简体字,但是转换成繁体后,大部分字的编码其实不变。再加上考虑另一个极端:纯音码可以无缝兼容简繁字形。那是不是只要形的部分越少,兼容性就越好?

在初期尝试的过程中,我很快确立了部首优先的原则,即第三码应该是部首,不是拆字拆出来的第一个部件。这是因为它有很多我最初甚至没有预料到的好处:

  1. 汉字中部首不出现在左边或上面的情况比想象中得多,可以提高三码离散。
    1. 如:「栽载哉」分别可以用 zlm zli zlk 输入,「墓幕暮慕募」分别可以用 mut muj muo mux mul 输入,「形型」分别是 xyp 和 xyt 而不会被「开」干扰
  2. 部首和字义有强关联,拆字时往往不用思考字形,而是只需要想到字义,就能打出这个字。
    1. 如:垄(与土有关),攀(与手有关),忧(与心有关),暮(与日有关),毫(与毛有关)
  3. 部首往往可以恰好把一类字的不同字形归类在一起。
    1. 如:「 劝勧劝 」都是qrl,「 忧忧 」都是ybx

最初的魔然字词码表是同时容纳简体和繁体的,但是后来发现虽然整体上一致性很高,但是高频字中还是有不少字三码不一致。很多用户反馈自己不会繁体字,需要一个纯简体的码表,于是这才另外制作了简化字版本。——笔者虽然日常打简化字不少,但是依然使用繁体版,所以笔者认为只要您不抗拒繁体字(碰到三码不一致的情况时,您的第一反应是「又学到了」而不是「又被坑到了」),都可以先尝试尝试繁体版,也许有意外之喜呢。

因此,我认为关于简繁字形的实验本身已经完结了。结论是:成功!虽不算 100% 完美,但完全可以接受,甚至超出预期。

The Unreasonable Effectiveness——为什么魔然如此好用

背景

如果您读了上两节,可能会注意到魔然一开始并不是为了「降低码长」「提高打字速度」等目标设计的,而是作者个人的一个小小实验品,单纯是为了实验音形码对简繁字形的兼容性。从另一个方面说,也是为了解决作者个人一个极小众的需求。

而其他设计,也都恰好只是这个需求催生来的——我只是希望音的部分够多(哪怕我长时间不用,也可以快速捡起来),而恰好我想少点选重,所以选择以「挂接用法」为蓝本而已。

当时(2023年初),我使用的是一款名为虎码的形码方案。我做魔然单纯是一个实验,打算在得到结论后,就回到虎码。唯一没想到的是,这个实验竟然相当成功,以至于我逐渐「乐不思虎」,在魔然扎根了,至今已经使用快 2 年。

但与之相对的是一个显然的悖论:自然码本身并不是一个追求性能的方案。单字重码率不低,词重更是不低。以通常视角来看,它不应该好用啊!

理论预言错得这么离谱,让我不得不做了一些分析:魔然究竟为什么好用?传统的评价指标到底「错」在哪?

或许,我们选择的指标根本不对——一个新的输入法评价指标

笔者认为,在全拼都能满足大部分用户的大背景下,汉字输入最重要的指标已经不是硬性的「码长」「确定性」等,而是一种(虚无缥缈的)「轻松感」「流畅感」,或者说,是实际输入中「输入法本身存在感的稀薄程度」。

Note:我认为任何输入法方案作者都应该注意到这一点:如果输入法评测所用的硬性数据指标真的那么重要,那全拼根本不应该成为最流行的输入法。

因此,任何评价输入法的指标都应该接受全拼检验:你所预言的「好用程度」必须与全拼的流行程度一致。假设你选取的评价标准得到的结论是「全拼是最差的输入法」,那必须解释为什么你的预言和现实不符。

如果以「输入法本身的存在感稀薄程度」为标准,我们可以立即注意到:

  1. 音码(以智能拼音和智能双拼为代表)的存在感在选重时会显现出来。
  2. 形码的存在感在 (1) 拆字 (2) 选重 时显现出来。

并且存在感会随时间累加:上面提到的事件如果耗时越长,存在感就越强。

因此,为了让输入更流畅,我们需要最小化拆字和选重的负担。下面我们来看看各输入法是怎么做的:

  1. 智能拼音:不断提高智能化程度,通过大量数据分析字词频率和关联,减少选字频率
  2. 形码
    1. 针对拆字的存在感:
      1. 强调训练高频字条反,从而降低拆字频率
      2. 设计更大的字根从而让拆字更直观,从而降低拆字用时
    2. 针对选重的存在感:设计更大的字根、更怪的拆字、乱序分布字根、出简让全、出简不出全等方法,从而降低选重率
      1. 或者可以直接打纯单,这样就完全没有词重带来的存在感了

那么魔然所属的双拼加形类别呢?

  1. 针对拆字的存在感:
    1. 通过设置简码,降低了拆字频率(一简和二简不需要拆字)
    2. 通过只取首末、字根音托,降低拆字耗时(思维负担的减少体现在时间缩短上)
    3. 通过置前两码为音码,拆字与输入音码可以同步进行,降低拆字的实际耗时
  2. 针对选重的存在感:
    1. 输入整句时,可以复用智能拼音的算法和一切成果
    2. 通过辅助码,可以降低选重的耗时
    3. 通过有策略地拆单,可以降低选重的频率

总的来说,双拼加形是对智能拼音的一个严格改进:它具有智能拼音的一切优势(从而有相同量级的选重频率),又有智能拼音所不具有的更多能力(这些能力可以降低选重频率),代价是一点学习成本。

在这里,我提出一个初步的量化方法:

存在感 = 选重频率 * 平均选重耗时 * w1 + 拆字频率 * 平均拆字耗时 * w2

(其中 w1 和 w2 是特定的加权系数,表示用户对这种存在感的厌恶程度:如果用户极度厌恶拆字(即提笔忘字还觉得这不是个事儿),那么他可以把 w2 设置为一个很大的数字。一般来说,为了讨论简便,不妨就设置 w1 = w2 = 1。)

「存在感」衡量了「仅仅因为输入法本身问题、导致用户额外付出的时间」(对不存在拆字和选重的方案也可以提出类似的指标),而在一个理想的输入法里,这个值应该是0。它并不衡量用户本身导致的输入错误问题(即键准不计入计算)。

事实上,在引入一些假设的前提下,存在感的确可以是 0:

  • 智能拼音用户在合适的文本上,可以做到 0。
  • 形码用户对所有字全部条反,只打纯单,可以做到 0。
  • 双拼加形用户预判所有重码,单字条反,可以做到 0。

上面的公式中并没有出现码长、手感等很多输入法发烧友熟悉的指标。或许……这并不是一个巧合?事实上,我们可能都有过这样的体会:用过一个方案,觉得它非常顺手,但是绝对速度却不快;或者用过一个方案,绝对速度不低,但是怎么用怎么觉得不爽。

因此,我认为「存在感」可以较好地从用户侧终端衡量输入法的实际体感好用程度。每个输入法的存在感值甚至可以通过实验测量出来。

  1. 这个指标可以用来测试用户本身。试设想以下场景:

    1. 某方案事实上是无理码,故在无外界帮助的情况下,拆字耗时可设想为无穷大,而用户不可避免地会碰到之前没打过的字,需要现拆,或
    2. 某拼音输入法每打一字都需要选字,且输入法的顺序完全错误,使得选字耗时极长

    则用户必然不会选择这样的方案。这意味着,用户必然存在一个「存在感耐受」的上限。
    上限可以用于选择最适合用户的方案:若用户通过估算或实验得到了自己的耐受上限,就可以选择存在感在该上限以下的、性能最佳的输入方案——也就是对自己来说最优的输入法。

  2. 这个指标也提供了一个评价方案的新维度。如果在任何情况下,方案A的绝对性能都较方案B更差,且存在感都更高,那么我们就有必要严肃地质疑这样的方案的意义为何了。

  3. 这个指标很有可能是不完备的。在本文中,我们假设 w1 和 w2 都只是用户本身的偏好,但这可能不对。例如,对拼音用户来说,选重是日常生活的一部分,所以碰到选重时不会有严重的抗拒心理;而对形码用户来说,任何一次非预期的选重都会带来严重的负反馈。这意味着上式中的 w1 和 w2 可能还与方案本身有关,而不只与用户有关。

或许,收益与难度不成正比——为易学性正名

从朴素的理解看来,一分耕耘一分收获,那么自然方案上限要高,自然也会更难。下更大力气练习,速度也会提升。那么我们是不是应该总是追求高上限、追求「更难」的方案呢?选择此类方案,意味着有更高的理论上限,就算自己不想练习到那么高的水准,也可以停在自己喜欢的水平上,以后想精进了可以随时精进。

在笔者看来,并非如此。甚至恰恰相反,大部分人应该更多看看那些「简单」的方案。我们不妨解构一下所谓的「难度」究竟是什么。以典型的形码为例,从一个用户的角度出发,要打一段话,用户需要进行这些活动:

  1. 断句分词;
  2. 对词语进行编码,进一步地,
  3. 对单字进行编码,将单字拆分为字根;
  4. 将字根映射到键盘键位上。

熟习形码的用户可能会立刻反对上面的说法:「不,我已经条反了单字/词语,所以我不需要再拆字/想字根了!」但假设一个用户从未进行专项学习和刻意练习,它将不得不经过这几道工序。事实上,通过刻意练习,从而跳过了一部分步骤,这正是所谓的 学习成本 。(当然,也正是因为字词有固定的打法,这样的刻意练习才成为可能。)

至此,我们把 方案难度 还原成了有明确定义的几项学习成本。你付出多少学习成本,就能跳过某些字词的某些步骤,从而提升速度。

接着,另一些形码爱好者或许会指出,上面的流程其实有些囫囵,如一些方案的字根是非常有规律的,而步骤 5 浑而言之,对这些方案不公平。另一些人也会指出,其实拆字往往不是直接拆到字根,而是拆成大块组件,然后再把大块组件拆成字根,这样学得更快。的确如此:根据排列组合的基本原理,流程越长,每个步骤中的学习成本就可以越少,而平均每字上的学习成本也越低。设有 n 个步骤,每个步骤的「学习成本」是 Ci ,则总成本是 i=1nCi ,而有固定打法的字有 O(i=1nCi) 个。

举一个简单的例子。假设我们要编码 10000 个字:

  • 方案 A:只有一个步骤,所以我们需要死记这 10000 个字的编码,成本为 10000 单位。
  • 方案 B:有两个步骤,每个步骤有 100 单位成本,总体上也可以覆盖 10000 个字,但成本只有 100+100=200。

但既然如此,我们为什么不构造一个步骤非常多、从而非常简单的方案呢?这是因为「步骤」的设计本身不能太天马行空,还是要符合人类的直觉。此外,为了让编码简单,一般应该让多个步骤的编码结果做非常简单的映射和组合,否则编码会非常困难。一个例子就是五笔的识别码,它的原理很简单,但还是被大部分用户诟病。

要点就在这里:步骤本身没法太多。因此,为了提升性能,许多方案会(1)增大步骤内的内容量 和/或 (2)减少步骤。预期是用户付出学习成本后,可以直接形成条反,而更少的步骤,可以强迫条反更快形成、生字编码时的反应回路更短。但上述简易模型和均值不等式指出,当我们尝试把步骤减半时,学习成本的上升远远超出 2 倍。以 10000 字为例,4 步骤的方案 A 成本可能是 4×100001/4=40,而 2 步骤的方案 B 成本是 10000×2=200,是 A 方案的整整 5 倍。

假设小张选择了 A 方案,而平行宇宙中的大张选择了 B 方案,那么在相同的练习量(字数)下,小张和大张都可以以高速打出条反了的常用字,但要学会剩下的字,大张需要付出远远超出小张的学习成本。更雪上加霜的是,这些字可能由于过于低频,而永远无法条反。也就是说小张更容易条反更多的字,最终在相同练习量时可以打出比大张更快的速度。换句话说,易学方案的「条反转化率」更高。

虽然上面的分析比较粗糙,但我相信应该可以论证:易学的方案在实践上更容易打出高速。至少这是有理论上的可能的。因此,笔者认为不应该盲目鄙视易学方案,更绝不应该在不追求高速时选择高难方案,因为理论分析已经指出「选择高难方案并停留在低水平」的性价比低于「直接选择一个易学的方案」。

或许,单字重码率没那么重要——双拼加形的特殊性

上一节的讨论是完全基於单字的,这是因为形码打词的基础也是单字——大部分形码的词语编码也无非是单字编码的拼合。

对双拼加形,表面上看词语也是单字编码的拼合,但事实上需要注意到双拼加形的单字编码是两种异质信息(音和形)的拼合。打词时,可以完全绕过形,即打词的学习成本其实反而低于打单,与形码是完全相反的:对很多人(包括笔者)来说,打词越多,思维负担越轻。而更有趣的是,打词的码长一般也比打单更短。综合来说,对双拼加形来说,打词速度更快。或者更直白地说,打词和打单是两种不同的心理过程,打词是其中更轻松的那个。

既然实际输入中打词是不可或缺的一部分,总体过程就是:断句分词→词语直接打音码→余下的单字通过打单的方式输入。在这种模式中,打单量其实并不多,而且大部分需要打的单字都是有语法功用的词,如「的」「地」「得」「了」「者」「式」「就」等,或者是一些频繁单用的字,如「打」「去」「走」「跑」「提」「喝」「吃」等。只要给这些字固定打法,其实日常打字的不确定性就已经减少很多了。

「那如果一定要打生僻字呢?重码率高怎么盲打?」这里或许我们应该反过来问:如果有大量生僻字本身是重码的,那么为什么要期望可以盲打「任何一个生僻字」呢?你在打这个字之前,是无法预判这个字有没有重码、是否需要选重的,所以无论你打哪个生僻字,都无法避免看候选窗。因此,不妨采取最激进的策略:干脆放弃所有生僻字的选重性能,为易学易用优化。

「难道词语(音码)就没有重码吗?」不,词语不仅有重码,而且还不少!但这些重码主要来自二字词。因此,打二字词时一般需要打辅助码,从而减少不确定性。

「但这不就又引入了新的难度了吗?」是的,但或许,我们可以不付出更多成本就能解决这个问题?

INFO

下面的讨论只适用于默认模式。

要解决词重,一个方法是引入越来越智能的语法模型。但魔然探索了另一条路。

魔然首创的「整句辅助码混输」(俗称「句中辅」)可以与固定简码联动,从而引入了一种特殊的功能—— 以单驭词

举例来说,一个熟习简码的用户,会习惯于使用 lig 输入「力」、用 lih 输入「利」。那么他在打词时,输入 lig 和 lih 其实也是下意识的,这就意味着,他可以在不付出任何额外适应和学习成本的前提下,完美区分:

  • 有利yblih 有力yblig
  • 权利qrlih 权力qrlig

另一方面,即使这个字在前面,也不用担心,只要无脑打字即可:

  • 易用yioys 已用yivys
  • 使用uiys 适用uizys

而这个学习过程是双向的。熟练打单字时,打词会直接得到几乎免费的加成。另一方面,即使对单字不熟悉,那么打词加辅打到第三码,也会对未来在打这个单字时有所加成。

换句话说,熟练掌握单字后,并不只能打单,还可以大幅提升打词的确定性。这与定长码的「单-词对立」不同,与「智能拼音挂接码表」不同,甚至与「直接辅助码输入法」不同。比较相近的是形码整句输入,但我相信到目前为止的讨论已经给你足够的启发。 😃

或许,选重并不影响确定性——次选的妙用

打字速度的一方面是重码率,它决定了我们打字过程中遇到的「惊喜」有多少。而另一方面就是码长,它在某种程度上更显著地影响实际打字速度。许多全拼用户反对双拼的理由就是,双拼没有简拼,而全拼使用简拼时,码长是很短的。他们是对的。至少对于一些常见场景来说,简拼的码长可以远远低于双拼。

在魔然中,根据笔者的实际打字实践,6 码输入三字词不仅准确度高,其实也没那么烦。而在输入一些超长词(大于 8 码)时,会感觉有点麻烦。因为以前用过定长码,就会觉得很多按键是没意义的——明明能 4 码解决,为什么要打那么多键?比如「维特根斯坦」,用简拼只要打 wtgst 就可以了,而双拼需要打 wztegfsitj 十码!按键越多,其实就越容易犯错,错了又要苦哈哈删改。

出于这种考虑,魔然默认开启了 inject_fixed_words 设置,在输入 4 码时可以注入码表里的长词,并且一定注入到次选位上。有趣的是,因为注入位置是固定的,反而导致默认模式在打长词时确定性比字词模式还要高!字词模式中,4 码的单字、二字词、长词全部混在一起,按频率排序,在实际看到候选之前其实很难预判到底按哪个键。但在默认模式的开箱设置中,4 码只要有词就一定是词;而对于长词,只要知道有这个词,一般只需要无脑按分号。比如上面提到的「维特根斯坦」,也只需要打 wtgt,比简拼还短。

这种设计不仅压缩了长词的码长,其实还提高了长词的输入确定性(相比字词模式)和误键可能性(相比打全双拼),可谓两全其美。

另一方面,为了避免从四码到整句输入时的割裂感,开箱设置中强烈倾向于让四码首选是二字词,且它是动态调频的。魔然预期在一段时间后,候选可以逐渐收敛到适合自己的顺序。对于少数不收敛的情况,可以使用 pin 功能强行固定,从而完全打造适合自己的词序。(当然,也可以直接打开「固词模式」。即使在固词模式下你也在养用户词库,这是魔然的一个绝技。)

结论

魔然方案体现了一种有别于以往的音形输入方案的设计取向。笔者认为魔然式的输入方案有如下特点:

  • 编码维度:结合通用双拼和直观形辅,降低学习成本。但是难以输入不知道读音的字。
  • 交互维度:以词语为主。「以单驭词」打破单字和词组的隔阂,将单字熟练度直接转换成区分重音词的能力。缺点是码长相比四码方案更长。
  • 性能维度:单字简码是主要的确定性来源。将长词注入 4 码次选位,不仅压缩码长,还通过固定注入位置提高了确定性。
  • 体感维度:承认低频字词输入的不可预测性,转而优化日常高频输入的流畅度(选重耗时和拆字耗时)。但选字无法完全避免。

这种设计取向并不适合所有人,但如果你认同这些理念,不妨一试!

The Dialectical Reversal——谈谈「简繁通打」

很多和我一样有传承字输入需求的用户,往往要么使用纯音码,要么使用纯形码。纯音码暂且不论,形码则往往只优先考虑一种字形,另一种字形则后置。对于需要经常切换两种字形的用户来说极为不便,因此便产生了所谓的「简繁通打」的说法:让简体字和繁体字的编码不重码。例如, 放在不同的按键上。典型的方案有徐码宇浩码

INFO

我个人认为「简繁通打」是有误导的宣传命名。没有细看定义的用户容易误解「只有简繁通打的方案才能输入繁体字/简体字」。但事实并非如此。一个不简繁通打的方案完全可以收录整个 Unicode 字集——那当然也可以输入繁体字。本节讨论中暂且继续使用该命名。

这类「简繁通打」方案声称自己解决了字形切换问题。当然,在一定程度上,的确如此。设若你需要临时输入繁(简)体字,直接打繁(简)体字编码即可。这类方案的实质是通过编码强行将「全等异体字」分离,目标是在无上下文/无模式切换的情况下,快速输入另一种异体字。

然而,在我看来,此类方案有着以下缺陷:

  • 若设置简码,而简码空间有限,则必然会优先一种字形。这又重新产生了简繁之间的不平等性。
    • 一些方案认为这是必要的妥协,提供多种简码表。
    • 一些方案认为这是严重的问题,直接放弃简码,用户应该只打全码。
    • 还有一些提供简繁混频排的简码表,但这会降低码表效率。(我曾见过某方案中 为为为 中的多个均被设置成一简。)
  • 需要额外记忆另一种字形的字根映射。若另一种字形用得不多,遗忘后会比较麻烦。
  • 由于简繁字形编码不同,如果需要大量输入另一种字形,则需要建立两套条反。

由于这些缺陷,私以为大字根的简繁通打方案的优化目标,其实受众很小——大概需要接近 1:1 比例日常输入简繁字形,以至于能建立两套稳定条反而不遗忘。但即使在这种情况下,学习成本也比较高。

问题在于,简繁之间一般是异体字的关系,而且大部分是全等异体字。所谓的全等异体字就是音义皆同,只有字形不同的一组字,如 就是一组全等异体字。但我们为什么一定要「分离」才能「通打」呢?是不是其实「合并」更容易「通打」?以前似乎没有人提出过这样的看法。

私以为答案是肯定的。要支持多种字形,同时还要降低使用负担,那么要做的不是「分离」,而应是「合并」。更需考虑到,简体和繁体只是全等异体字的一小个侧面。对于一个字来说,它可以有不止两种异体。具体选用哪种异体字,是由「正字法」给出的——大陆的规范字、台湾的正体字、香港用字标准、日本当用汉字表等,都是「正字法」。

假如说,我们恰好可以让全等异体字重码(与「简繁通打」类方案恰恰相反),那「正字法」就可以通过一套配置文件给出。通过简单的模式切换,就可以轻松输出「简」「繁」「日」乃至别的正字法标准,同时我们思维上并不做任何切换,并且输入效率不受影响。

也就是说,我们需要追求的是「全等异体字之间」恰恰应该重码,而不是全等异体字关系的字之间应该不重码。

自然码巧妙的设计几乎完美实现了这个目标。双拼的 2 码音码和自然码的「部首优先」规则,使得绝大多数字全等异体字都具有相同的 3 码。如在使用魔然时打开「字集增广模式」,往往在输入 3 码时,可以看到一大批形似的字,并且一般还真就是异体字关系。如输入 lqe 可以看到「 陆陆𲉊𨸪𨽐𨽰 」,只有一个「 𨻧 」不属其列。更神奇的是,很多异体字 4 码也真的就刚好重码(试试输入 yjtmo)。

总的来说,在异体字处理上,我认为(受控的)「合」远远好于「分」。「分」唯一的好处是不用切换模式,但收益似乎并不比代价更大。所以我不认同「简繁通打」。

INFO

开放问题:可否设计一种方案,前 N 码恰好归类全等异体字的等价类,而输入 N+M 码又可以把同一个等价类中的字区分开?