198 lines
5.3 KiB
TypeScript
198 lines
5.3 KiB
TypeScript
/**
|
|
* 答案推導工具 - 從例句和挖空例句中動態推導正確答案
|
|
*/
|
|
|
|
export interface AnswerExtractionResult {
|
|
answers: string[];
|
|
isValid: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
/**
|
|
* 從例句和挖空題目中提取答案
|
|
* @param example 原始例句
|
|
* @param filledQuestion 挖空後的題目
|
|
* @returns 提取的答案陣列
|
|
*/
|
|
export function extractAnswerFromBlanks(example: string, filledQuestion: string): AnswerExtractionResult {
|
|
try {
|
|
// 輸入驗證
|
|
if (!example || !filledQuestion) {
|
|
return {
|
|
answers: [],
|
|
isValid: false,
|
|
error: "例句或挖空題目為空"
|
|
};
|
|
}
|
|
|
|
if (!filledQuestion.includes('____')) {
|
|
return {
|
|
answers: [],
|
|
isValid: false,
|
|
error: "挖空題目中沒有找到 ____"
|
|
};
|
|
}
|
|
|
|
// 方法1: 正則匹配法 (推薦用於單個空格)
|
|
if (filledQuestion.split('____').length === 2) {
|
|
return extractSingleBlankAnswer(example, filledQuestion);
|
|
}
|
|
|
|
// 方法2: 差異比對法 (用於多個空格)
|
|
return extractMultipleBlanksAnswers(example, filledQuestion);
|
|
|
|
} catch (error) {
|
|
return {
|
|
answers: [],
|
|
isValid: false,
|
|
error: `答案提取失敗: ${error instanceof Error ? error.message : '未知錯誤'}`
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 提取單個空格的答案 (正則匹配法)
|
|
*/
|
|
function extractSingleBlankAnswer(example: string, filledQuestion: string): AnswerExtractionResult {
|
|
try {
|
|
// 轉義特殊字符並替換 ____ 為捕獲群組
|
|
const escapedPattern = filledQuestion
|
|
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // 轉義正則特殊字符
|
|
.replace(/____/g, '(.+?)'); // 替換為非貪婪捕獲群組
|
|
|
|
const regex = new RegExp(`^${escapedPattern}$`, 'i');
|
|
const match = example.match(regex);
|
|
|
|
if (match && match[1]) {
|
|
const answer = match[1].trim();
|
|
return {
|
|
answers: [answer],
|
|
isValid: true
|
|
};
|
|
}
|
|
|
|
// 如果完全匹配失敗,嘗試部分匹配
|
|
const partialRegex = new RegExp(escapedPattern, 'i');
|
|
const partialMatch = example.match(partialRegex);
|
|
|
|
if (partialMatch && partialMatch[1]) {
|
|
const answer = partialMatch[1].trim();
|
|
return {
|
|
answers: [answer],
|
|
isValid: true
|
|
};
|
|
}
|
|
|
|
return {
|
|
answers: [],
|
|
isValid: false,
|
|
error: "無法匹配例句和挖空題目"
|
|
};
|
|
|
|
} catch (error) {
|
|
return {
|
|
answers: [],
|
|
isValid: false,
|
|
error: `正則匹配失敗: ${error instanceof Error ? error.message : '未知錯誤'}`
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 提取多個空格的答案 (差異比對法)
|
|
*/
|
|
function extractMultipleBlanksAnswers(example: string, filledQuestion: string): AnswerExtractionResult {
|
|
try {
|
|
const parts = filledQuestion.split('____');
|
|
const answers: string[] = [];
|
|
|
|
let currentPos = 0;
|
|
|
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
const beforePart = parts[i];
|
|
const afterPart = parts[i + 1];
|
|
|
|
// 找到前半部分的結束位置
|
|
const startPos = currentPos + beforePart.length;
|
|
|
|
// 找到後半部分的開始位置
|
|
let endPos: number;
|
|
if (afterPart === '') {
|
|
// 如果是最後一個空格,到句子結尾
|
|
endPos = example.length;
|
|
} else {
|
|
endPos = example.indexOf(afterPart, startPos);
|
|
if (endPos === -1) {
|
|
return {
|
|
answers: [],
|
|
isValid: false,
|
|
error: `無法找到後半部分: "${afterPart}"`
|
|
};
|
|
}
|
|
}
|
|
|
|
// 提取中間的詞作為答案
|
|
const answer = example.substring(startPos, endPos).trim();
|
|
answers.push(answer);
|
|
|
|
currentPos = endPos;
|
|
}
|
|
|
|
return {
|
|
answers,
|
|
isValid: answers.length > 0 && answers.every(ans => ans.length > 0)
|
|
};
|
|
|
|
} catch (error) {
|
|
return {
|
|
answers: [],
|
|
isValid: false,
|
|
error: `多空格提取失敗: ${error instanceof Error ? error.message : '未知錯誤'}`
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 獲取填空題的第一個答案 (最常用)
|
|
* @param example 原始例句
|
|
* @param filledQuestion 挖空後的題目
|
|
* @param fallbackAnswer 降級答案 (通常是 word 屬性)
|
|
* @returns 正確答案字串
|
|
*/
|
|
export function getCorrectAnswer(
|
|
example: string,
|
|
filledQuestion: string | undefined,
|
|
fallbackAnswer: string
|
|
): string {
|
|
if (!filledQuestion) {
|
|
return fallbackAnswer;
|
|
}
|
|
|
|
const result = extractAnswerFromBlanks(example, filledQuestion);
|
|
|
|
if (result.isValid && result.answers.length > 0) {
|
|
return result.answers[0];
|
|
}
|
|
|
|
// 推導失敗時使用降級答案
|
|
console.warn('答案推導失敗,使用降級答案:', result.error);
|
|
return fallbackAnswer;
|
|
}
|
|
|
|
/**
|
|
* 驗證用戶答案是否正確
|
|
* @param userAnswer 用戶輸入的答案
|
|
* @param example 原始例句
|
|
* @param filledQuestion 挖空後的題目
|
|
* @param fallbackAnswer 降級答案
|
|
* @returns 是否正確
|
|
*/
|
|
export function validateAnswer(
|
|
userAnswer: string,
|
|
example: string,
|
|
filledQuestion: string | undefined,
|
|
fallbackAnswer: string
|
|
): boolean {
|
|
const correctAnswer = getCorrectAnswer(example, filledQuestion, fallbackAnswer);
|
|
return userAnswer.toLowerCase().trim() === correctAnswer.toLowerCase().trim();
|
|
} |