diff --git "a/src/lib/\345\243\223\347\270\256\350\241\250\347\244\272.ts" "b/src/lib/\345\243\223\347\270\256\350\241\250\347\244\272.ts" index 62706b9..e9ad153 100644 --- "a/src/lib/\345\243\223\347\270\256\350\241\250\347\244\272.ts" +++ "b/src/lib/\345\243\223\347\270\256\350\241\250\347\244\272.ts" @@ -23,14 +23,14 @@ const 韻序表 = [ */ export function encode音韻編碼(地位: 音韻地位): string { const { 母, 呼, 等, 類, 韻, 聲 } = 地位; - const 母序 = 所有.母.indexOf(母); + const 母序 = [...所有.母].indexOf(母); const 韻序 = 韻序表.indexOf(韻) + +([...'東歌麻庚'].includes(韻) && !['一', '二'].includes(等)); // NOTE the value `-1` is expected when the argument is `null` - const 呼序 = 所有.呼.indexOf(呼!) + 1; - const 類序 = 所有.類.indexOf(類!) + 1; + const 呼序 = [...所有.呼].indexOf(呼!) + 1; + const 類序 = [...所有.類].indexOf(類!) + 1; - const 呼類聲序 = (呼序 << 4) | (類序 << 2) | 所有.聲.indexOf(聲); + const 呼類聲序 = (呼序 << 4) | (類序 << 2) | [...所有.聲].indexOf(聲); return 編碼表[母序] + 編碼表[韻序] + 編碼表[呼類聲序]; } @@ -55,8 +55,8 @@ export function decode音韻編碼(編碼: string): 音韻地位 { assert(index !== -1, () => `Invalid character in 編碼: ${JSON.stringify(ch)}`); return index; }); - assert(母序 < 所有.母.length, () => `Invalid 母序號: ${母序}`); - const 母 = 所有.母[母序]; + assert(母序 < [...所有.母].length, () => `Invalid 母序號: ${母序}`); + const 母 = [...所有.母][母序]; assert(韻序 < 韻序表.length, () => `Invalid 韻序號: ${韻序}`); let 韻 = 韻序表[韻序]; @@ -65,7 +65,7 @@ export function decode音韻編碼(編碼: string): 音韻地位 { } let 等: string; for (const [韻等, 各韻] of Object.entries(等韻搭配)) { - if (各韻.includes(韻)) { + if (各韻.has(韻)) { 等 = 韻等[+(韻序表[韻序] === '*')]; if (等 === '三' && [...'端透定泥'].includes(母)) { 等 = '四'; @@ -75,15 +75,15 @@ export function decode音韻編碼(編碼: string): 音韻地位 { } const 呼序 = 呼類聲序 >> 4; - assert(呼序 <= 所有.呼.length, () => `Invalid 呼序號: ${呼序}`); - const 呼 = 呼序 ? 所有.呼[呼序 - 1] : null; + assert(呼序 <= [...所有.呼].length, () => `Invalid 呼序號: ${呼序}`); + const 呼 = 呼序 ? [...所有.呼][呼序 - 1] : null; const 類序 = (呼類聲序 >> 2) & 0b11; - assert(類序 <= 所有.類.length, () => `Invalid 類序號: ${類序}`); - const 類 = 類序 ? 所有.類[類序 - 1] : null; + assert(類序 <= [...所有.類].length, () => `Invalid 類序號: ${類序}`); + const 類 = 類序 ? [...所有.類][類序 - 1] : null; const 聲序 = 呼類聲序 & 0b11; - const 聲 = 所有.聲[聲序]; + const 聲 = [...所有.聲][聲序]; // NOTE type assertion safe because the constructor checks it return new 音韻地位(母, 呼, 等!, 類, 韻, 聲, _UNCHECKED); diff --git "a/src/lib/\345\270\270\347\224\250\350\241\250\351\201\224\345\274\217.ts" "b/src/lib/\345\270\270\347\224\250\350\241\250\351\201\224\345\274\217.ts" index a22739e..ec29be4 100644 --- "a/src/lib/\345\270\270\347\224\250\350\241\250\351\201\224\345\274\217.ts" +++ "b/src/lib/\345\270\270\347\224\250\350\241\250\351\201\224\345\274\217.ts" @@ -15,31 +15,31 @@ import { 呼韻搭配, 等韻搭配 } from './音韻屬性常量'; /** 一等韻 */ -export const 一等韻 = 等韻搭配.一.join('') + '韻'; +export const 一等韻 = [...等韻搭配.一].join('') + '韻'; /** 二等韻 */ -export const 二等韻 = 等韻搭配.二.join('') + '韻'; +export const 二等韻 = [...等韻搭配.二].join('') + '韻'; /** 三等韻(注意:拼端組時為四等) */ -export const 三等韻 = 等韻搭配.三.join('') + '韻'; +export const 三等韻 = [...等韻搭配.三].join('') + '韻'; /** 四等韻 */ -export const 四等韻 = 等韻搭配.四.join('') + '韻'; +export const 四等韻 = [...等韻搭配.四].join('') + '韻'; /** 一三等韻 */ -export const 一三等韻 = 等韻搭配.一三.join('') + '韻'; +export const 一三等韻 = [...等韻搭配.一三].join('') + '韻'; /** 二三等韻(注意:拼端組時為二四等) */ -export const 二三等韻 = 等韻搭配.二三.join('') + '韻'; +export const 二三等韻 = [...等韻搭配.二三].join('') + '韻'; /** * 韻內分開合口的韻 */ -export const 分開合韻 = 呼韻搭配.開合.join('') + '韻'; +export const 分開合韻 = [...呼韻搭配.開合].join('') + '韻'; /** * 僅為開口的韻(含之、魚韻及效、深、咸攝諸韻) */ -export const 開口韻 = 呼韻搭配.開.join('') + '韻'; +export const 開口韻 = [...呼韻搭配.開].join('') + '韻'; /** * 僅為合口的韻 */ -export const 合口韻 = 呼韻搭配.合.join('') + '韻'; +export const 合口韻 = [...呼韻搭配.合].join('') + '韻'; /** * 開合中立韻(東冬鍾江模尤侯) */ -export const 開合中立韻 = 呼韻搭配.中立.join('') + '韻'; +export const 開合中立韻 = [...呼韻搭配.中立].join('') + '韻'; diff --git "a/src/lib/\351\237\263\351\237\273\345\234\260\344\275\215.ts" "b/src/lib/\351\237\263\351\237\273\345\234\260\344\275\215.ts" index 97f0ad7..6f82d94 100644 --- "a/src/lib/\351\237\263\351\237\273\345\234\260\344\275\215.ts" +++ "b/src/lib/\351\237\263\351\237\273\345\234\260\344\275\215.ts" @@ -3,19 +3,19 @@ import { 母到清濁, 母到組, 母到音, 韻到攝 } from './拓展音韻屬 import { 呼韻搭配, 所有, 等母搭配, 等韻搭配, 鈍音母, 陰聲韻 } from './音韻屬性常量'; const pattern描述 = new RegExp( - `^([${所有.母.join('')}])([${所有.呼.join('')}]?)([${所有.等.join('')}]?)` + - `([${所有.類.join('')}]?)([${所有.韻.join('')}])([${所有.聲.join('')}])$`, + `^([${[...所有.母].join('')}])([${[...所有.呼].join('')}]?)([${[...所有.等].join('')}]?)` + + `([${[...所有.類].join('')}]?)([${[...所有.韻].join('')}])([${[...所有.聲].join('')}])$`, 'u', ); // for 音韻地位.屬於 const 表達式屬性可取值 = { ...所有, - 音: [...'脣舌齒牙喉'] as const, - 攝: [...'通江止遇蟹臻山效果假宕梗曾流深咸'] as const, - 組: [...'幫端知精莊章見影'] as const, + 音: new Set('脣舌齒牙喉'), + 攝: new Set('通江止遇蟹臻山效果假宕梗曾流深咸'), + 組: new Set('幫端知精莊章見影'), }; -const 鈍音組 = [...'幫見影'] as const; +const 鈍音組 = new Set('幫見影'); /** * @see {@link 音韻地位.判斷} @@ -311,7 +311,7 @@ export class 音韻地位 { */ get 韻別(): string { const { 韻, 聲 } = this; - return 陰聲韻.includes(韻) ? '陰' : 聲 === '入' ? '入' : '陽'; + return 陰聲韻.has(韻) ? '陰' : 聲 === '入' ? '入' : '陽'; } /** @@ -376,12 +376,12 @@ export class 音韻地位 { } if (呼 === '合' && 母 === '云') { 呼 = null; - } else if (呼 && 呼韻搭配[呼 as '開' | '合'].includes(韻)) { + } else if (呼 && 呼韻搭配[呼 as '開' | '合'].has(韻)) { 呼 = null; } - if (等 === '三' && [...'羣邪俟'].includes(母)) { + if (等 === '三' && new Set('羣邪俟').has(母)) { 等 = ''; - } else if (等母搭配.三.includes(母) || ![...等韻搭配.一三, ...等韻搭配.二三].includes(韻)) { + } else if (等母搭配.三.has(母) || ![...等韻搭配.一三, ...等韻搭配.二三].includes(韻)) { 等 = ''; } return 母 + (呼 ?? '') + 等 + (類 ?? '') + 韻 + 聲; @@ -451,9 +451,9 @@ export class 音韻地位 { */ get 韻圖等(): string { const { 母, 等, 類 } = this; - if ([...'莊初崇生俟'].includes(母)) { + if (new Set('莊初崇生俟').has(母)) { return '二'; - } else if (類 === 'A' || (等 === '三' && [...'精清從心邪以'].includes(母))) { + } else if (類 === 'A' || (等 === '三' && new Set('精清從心邪以').has(母))) { return '四'; } else { return 等; @@ -609,13 +609,13 @@ export class 音韻地位 { if (/^不分類$/.exec(token)) return 類 === null; if ((match = /^(清|濁)音$/.exec(token))) return 清濁[1] === match[1]; if ((match = /^[全次][清濁]$/.exec(token))) return 清濁 === match[0]; - if (/^鈍音$/.exec(token)) return 鈍音組.includes(組!); - if (/^銳音$/.exec(token)) return !鈍音組.includes(組!); + if (/^鈍音$/.exec(token)) return 鈍音組.has(組!); + if (/^銳音$/.exec(token)) return !鈍音組.has(組!); if ((match = /^(.+?)([母等類韻音攝組聲])$/.exec(token))) { const values = [...match[1]]; const key = match[2] as keyof typeof 表達式屬性可取值; const possibleValues = 表達式屬性可取值[key]; - const invalidValues = values.filter(i => !possibleValues.includes(i)); + const invalidValues = values.filter(i => !possibleValues.has(i)); if (invalidValues.length) { throw new Error(`unknown ${key}: ${invalidValues.join(', ')}`); } @@ -799,8 +799,8 @@ export class 音韻地位 { * > 音韻地位 = TshetUinh.音韻地位.from描述('幫三C凡入'); * > 音韻地位.判斷([ * > ['遇果假攝 或 支脂之佳韻', ''], - * > ['蟹攝 或 微韻', 'i'], - * > ['效流攝', 'u'], + * > ['蟹攝 或 微韻', 'j'], + * > ['效流攝', 'w'], * > ['深咸攝', [ * > ['舒聲', 'm'], * > ['入聲', 'p'] @@ -959,7 +959,7 @@ export class 音韻地位 { ['韻', 韻], ['聲', 聲], ] as const) { - if (!((值 === null && !!nullable) || 所有[屬性].includes(值!))) { + if (!((值 === null && !!nullable) || 所有[屬性].has(值!))) { const suggestion = ( { 母: { 娘: '孃', 群: '羣' }, @@ -974,36 +974,36 @@ export class 音韻地位 { // 順序:從搭配規則從基本到精細 // 聲(僅韻-聲搭配) - 聲 === '入' && 陰聲韻.includes(韻) && reject(`unexpected ${韻}韻入聲`); + 聲 === '入' && 陰聲韻.has(韻) && reject(`unexpected ${韻}韻入聲`); // 等、呼、類(基本) // 母-等 for (const [搭配等, 搭配母] of Object.entries(等母搭配)) { - if (搭配母.includes(母)) { + if (搭配母.has(母)) { [...搭配等].includes(等) || reject(`unexpected ${母}母${等}等`); } } // 等-韻 for (const [搭配各等, 搭配各韻] of Object.entries(等韻搭配)) { - if (搭配各韻.includes(韻)) { + if (搭配各韻.has(韻)) { if ([...搭配各等].includes(等)) { break; - } else if (搭配各等.includes('三') && 等 === '四' && [...'端透定泥'].includes(母)) { + } else if (搭配各等.includes('三') && 等 === '四' && new Set('端透定泥').has(母)) { break; } reject(`unexpected ${韻}韻${等}等`); } } // 母-呼(基本)、呼-韻 - if ([...'幫滂並明'].includes(母)) { + if (new Set('幫滂並明').has(母)) { 呼 && reject('unexpected 呼 for 脣音'); - } else if (呼韻搭配.中立.includes(韻)) { + } else if (呼韻搭配.中立.has(韻)) { 呼 && reject('unexpected 呼 for 開合中立韻'); - } else if (呼韻搭配.開合.includes(韻)) { + } else if (呼韻搭配.開合.has(韻)) { 呼 ?? reject('missing 呼'); } else { for (const 搭配呼 of ['開', '合'] as const) { - if (呼韻搭配[搭配呼].includes(韻)) { + if (呼韻搭配[搭配呼].has(韻)) { if (呼 === 搭配呼) { break; } else if (呼) { @@ -1017,7 +1017,7 @@ export class 音韻地位 { // 母-類(基本)、等-類、類-韻(基本) if (等 !== '三') { 類 && reject('unexpected 類 for 非三等'); - } else if (!鈍音母.includes(母)) { + } else if (!鈍音母.has(母)) { 類 && reject('unexpected 類 for 銳音聲母'); } else { const [典型搭配類, 搭配類] = 類搭配(母, 呼, 韻); @@ -1030,17 +1030,17 @@ export class 音韻地位 { } // 母-韻 - if ([...'幫滂並明'].includes(母)) { - [...'之魚殷痕嚴'].includes(韻) && reject(`unexpected ${韻}韻脣音`); + if (new Set('幫滂並明').has(母)) { + new Set('之魚殷痕嚴').has(韻) && reject(`unexpected ${韻}韻脣音`); } else { 韻 === '凡' && reject(`unexpected 凡韻非脣音`); } - if ([...'莊初崇生俟'].includes(母)) { + if (new Set('莊初崇生俟').has(母)) { 等 === '三' && ['麻', '清', '幽'].includes(韻) && reject(`unexpected ${韻}韻${韻 === '麻' ? '三等' : ''}莊組`); 呼 === '開' && ['真', '殷'].includes(韻) && reject(`unexpected ${韻}韻開口莊組`); } else { 韻 === '臻' && reject(`unexpected 臻韻非莊組`); - if (!鈍音母.includes(母)) { + if (!鈍音母.has(母)) { 韻 === '庚' && 等 !== '二' && reject(`unexpected 庚韻${等}等${母}母`); 韻 === '蒸' && 呼 === '合' && reject(`unexpected 蒸韻合口${母}母`); } @@ -1058,20 +1058,15 @@ export class 音韻地位 { const marginalTests = [ ['陽韻A類', true, 韻 === '陽' && 類 === 'A', '陽韻A類'], - [ - '端組類隔', - true, - [...'端透定泥'].includes(母) && (等 === '二' || (等 === '四' && !等韻搭配.四.includes(韻))), - `${韻}韻${等}等${母}母`, - ], - ['咍韻脣音', true, 韻 === '咍' && [...'幫滂並明'].includes(母), `咍韻脣音`], + ['端組類隔', true, new Set('端透定泥').has(母) && (等 === '二' || (等 === '四' && !等韻搭配.四.has(韻))), `${韻}韻${等}等${母}母`], + ['咍韻脣音', true, 韻 === '咍' && new Set('幫滂並明').has(母), `咍韻脣音`], ['匣母三等', true, 母 === '匣' && 等 === '三', `匣母三等`], - ['羣邪俟母非三等', true, 等 !== '三' && [...'羣邪俟'].includes(母), `${母}母${等}等`], + ['羣邪俟母非三等', true, 等 !== '三' && new Set('羣邪俟').has(母), `${母}母${等}等`], ['云母開口', false, 母 === '云' && 呼 === '開' && ![...'宵幽侵鹽嚴'].includes(韻), '云母開口'], [ '蒸幽韻特殊類', false, - 類 && ['蒸', '幽'].includes(韻) && ([...'幫滂並明'].includes(母) ? 類 !== 'B' : 呼 === '開' && 類 === 'B'), + 類 && ['蒸', '幽'].includes(韻) && (new Set('幫滂並明').has(母) ? 類 !== 'B' : 呼 === '開' && 類 === 'B'), `${韻}韻${母}母${類}類`, ], ] as const; @@ -1121,12 +1116,12 @@ export class 音韻地位 { const 聲 = match[6]; if (簡略描述) { - if (!呼 && ![...'幫滂並明'].includes(母)) { - if (母 === '云' && 呼韻搭配.開合.includes(韻)) { + if (!呼 && !new Set('幫滂並明').has(母)) { + if (母 === '云' && 呼韻搭配.開合.has(韻)) { 呼 = '合'; } else { for (const 搭配呼 of ['開', '合'] as const) { - if (呼韻搭配[搭配呼].includes(韻)) { + if (呼韻搭配[搭配呼].has(韻)) { 呼 = 搭配呼; break; } @@ -1139,8 +1134,8 @@ export class 音韻地位 { 等 = '三'; } else { for (const 搭配等 of ['一', '二', '三', '四'] as const) { - if (等韻搭配[搭配等].includes(韻)) { - if (搭配等 === '三' && [...'端透定泥'].includes(母)) { + if (等韻搭配[搭配等].has(韻)) { + if (搭配等 === '三' && new Set('端透定泥').has(母)) { 等 = '四'; } else { 等 = 搭配等; @@ -1151,7 +1146,7 @@ export class 音韻地位 { } } - if (!類 && 等 === '三' && 鈍音母.includes(母)) { + if (!類 && 等 === '三' && 鈍音母.has(母)) { const [典型搭配類] = 類搭配(母, 呼, 韻); if (典型搭配類.length === 1) { 類 = 典型搭配類; @@ -1177,21 +1172,21 @@ function 類搭配(母: string, 呼: string | null, 韻: string): [string, strin return ['B', 'B']; } const 含邊緣地位類 = 韻 === '蒸' ? 'CB' : 'AB'; - if ([...'幫滂並明'].includes(母)) { + if (new Set('幫滂並明').has(母)) { return ['B', 含邊緣地位類]; } else { return [含邊緣地位類[0], 含邊緣地位類]; } } for (const [搭配類, 搭配韻] of [ - ['C', [...'東鍾之微魚虞廢殷元文歌尤嚴凡']], - ['AB', [...'支脂祭真仙宵侵鹽']], // 幽 already handled above (same for 蒸 & 陽) - ['A', [...'麻清']], - ['B', [...'庚']], - //['CB', [...'蒸']], - //['CA', [...'陽']], + ['C', new Set('東鍾之微魚虞廢殷元文歌尤嚴凡')], + ['AB', new Set('支脂祭真仙宵侵鹽')], // 幽 already handled above (same for 蒸 & 陽) + ['A', new Set('麻清')], + ['B', new Set('庚')], + //['CB', new Set('蒸')], + //['CA', new Set('陽')], ] as const) { - if (搭配韻.includes(韻)) { + if (搭配韻.has(韻)) { return [搭配類, 搭配類]; } } diff --git "a/src/lib/\351\237\263\351\237\273\345\261\254\346\200\247\345\270\270\351\207\217.ts" "b/src/lib/\351\237\263\351\237\273\345\261\254\346\200\247\345\270\270\351\207\217.ts" index 7a2ee79..b1edaf6 100644 --- "a/src/lib/\351\237\263\351\237\273\345\261\254\346\200\247\345\270\270\351\207\217.ts" +++ "b/src/lib/\351\237\263\351\237\273\345\261\254\346\200\247\345\270\270\351\207\217.ts" @@ -1,41 +1,41 @@ /** 全部六要素之枚舉 */ export const 所有 = { - 母: [...'幫滂並明端透定泥來知徹澄孃精清從心邪莊初崇生俟章昌常書船日見溪羣疑影曉匣云以'], - 呼: [...'開合'], - 等: [...'一二三四'], - 類: [...'ABC'], - 韻: [...'東冬鍾江支脂之微魚虞模齊祭泰佳皆夬灰咍廢真臻文殷元魂痕寒刪山先仙蕭宵肴豪歌麻陽唐庚耕清青蒸登尤侯幽侵覃談鹽添咸銜嚴凡'], - 聲: [...'平上去入'], + 母: new Set('幫滂並明端透定泥來知徹澄孃精清從心邪莊初崇生俟章昌常書船日見溪羣疑影曉匣云以'), + 呼: new Set('開合'), + 等: new Set('一二三四'), + 類: new Set('ABC'), + 韻: new Set('東冬鍾江支脂之微魚虞模齊祭泰佳皆夬灰咍廢真臻文殷元魂痕寒刪山先仙蕭宵肴豪歌麻陽唐庚耕清青蒸登尤侯幽侵覃談鹽添咸銜嚴凡'), + 聲: new Set('平上去入'), } as const; /** 幫見影組聲母,在三等分ABC類 */ -export const 鈍音母 = [...'幫滂並明見溪羣疑影曉匣云'] as const; +export const 鈍音母 = new Set('幫滂並明見溪羣疑影曉匣云'); -export const 陰聲韻 = [...'支脂之微魚虞模齊祭泰佳皆夬灰咍廢蕭宵肴豪歌麻侯尤幽'] as const; +export const 陰聲韻 = new Set('支脂之微魚虞模齊祭泰佳皆夬灰咍廢蕭宵肴豪歌麻侯尤幽'); /** 依可搭配的等列出各韻 */ export const 等韻搭配 = { - 一: [...'冬模泰灰咍魂痕寒豪唐登侯覃談'], - 二: [...'江佳皆夬刪山肴耕咸銜'], - 三: [...'鍾支脂之微魚虞祭廢真臻文殷元仙宵陽清蒸尤幽侵鹽嚴凡'], - 四: [...'齊先蕭青添'], - 一三: [...'東歌'], - 二三: [...'麻庚'], + 一: new Set('冬模泰灰咍魂痕寒豪唐登侯覃談'), + 二: new Set('江佳皆夬刪山肴耕咸銜'), + 三: new Set('鍾支脂之微魚虞祭廢真臻文殷元仙宵陽清蒸尤幽侵鹽嚴凡'), + 四: new Set('齊先蕭青添'), + 一三: new Set('東歌'), + 二三: new Set('麻庚'), } as const; /** 依可搭配的呼列出各韻 */ export const 呼韻搭配 = { - 開合: [...'支脂微齊祭泰佳皆夬廢真元寒刪山先仙歌麻陽唐庚耕清青蒸登'], - 開: [...'之魚咍臻殷痕蕭宵肴豪幽侵覃談鹽添咸銜嚴'], - 合: [...'虞灰文魂凡'], - 中立: [...'東冬鍾江模尤侯'], + 開合: new Set('支脂微齊祭泰佳皆夬廢真元寒刪山先仙歌麻陽唐庚耕清青蒸登'), + 開: new Set('之魚咍臻殷痕蕭宵肴豪幽侵覃談鹽添咸銜嚴'), + 合: new Set('虞灰文魂凡'), + 中立: new Set('東冬鍾江模尤侯'), } as const; /** 依可搭配的等列出各母,包含邊緣搭配 */ export const 等母搭配 = { - 一二三四: [...'幫滂並明來見溪羣疑影曉匣'], - 二三: [...'知徹澄孃莊初崇生俟'], - 一三四: [...'精清從心邪'], - 三: [...'章昌常書船日云以'], - 一二四: [...'端透定泥'], + 一二三四: new Set('幫滂並明來見溪羣疑影曉匣'), + 二三: new Set('知徹澄孃莊初崇生俟'), + 一三四: new Set('精清從心邪'), + 三: new Set('章昌常書船日云以'), + 一二四: new Set('端透定泥'), } as const;