리펙토링: 코드 최적화

This commit is contained in:
2026-05-31 16:21:55 +09:00
parent 6300fe1082
commit de0a92d10f
11 changed files with 130 additions and 156 deletions
+48
View File
@@ -0,0 +1,48 @@
export const ACTIVE_DISTRIBUTION_COLOR = "#f97316";
function colorLatex(latex: string, color: string) {
return `\\textcolor{${color}}{${latex}}`;
}
interface FormatParenthesisExpressionLatexParams<T> {
multiplier: number;
terms: T[];
operators: ("+" | "-")[];
formatTermLatex: (term: T) => string;
activeTermIndex?: number;
activeColor?: string;
}
export function formatParenthesisExpressionLatex<T>({
multiplier,
terms,
operators,
formatTermLatex,
activeTermIndex,
activeColor = ACTIVE_DISTRIBUTION_COLOR,
}: FormatParenthesisExpressionLatexParams<T>) {
const hasActiveTerm = activeTermIndex !== undefined;
const maybeColor = (latex: string, active: boolean) =>
hasActiveTerm && active ? colorLatex(latex, activeColor) : latex;
const termLatex = terms
.map((term, index) => {
const displayTerm = maybeColor(
formatTermLatex(term),
index === activeTermIndex,
);
if (index === 0) return displayTerm;
return `${operators[index - 1]} ${displayTerm}`;
})
.join(" ");
if (hasActiveTerm) {
return `${colorLatex(`${multiplier}`, activeColor)}${colorLatex(
"(",
activeColor,
)}${termLatex}${colorLatex(")", activeColor)}`;
}
return `${multiplier}\\left(${termLatex}\\right)`;
}
+16 -17
View File
@@ -14,22 +14,26 @@ describe("parenthesis sign problem engine", () => {
expect(Number.isInteger(problem.multiplier)).toBe(true);
expect(problem.multiplier).toBeGreaterThanOrEqual(-9);
expect(problem.multiplier).toBeLessThanOrEqual(-1);
expect(problem.a).toBeGreaterThanOrEqual(1);
expect(problem.a).toBeLessThanOrEqual(9);
expect(problem.b).toBeGreaterThanOrEqual(1);
expect(problem.b).toBeLessThanOrEqual(9);
expect(problem.a).not.toBe(problem.b);
expect(["+", "-"]).toContain(problem.operator);
expect(problem.terms).toHaveLength(2);
expect(problem.operators).toHaveLength(1);
expect(problem.terms.every((term) => term >= 1 && term <= 9)).toBe(true);
expect(new Set(problem.terms).size).toBe(problem.terms.length);
expect(["+", "-"]).toContain(problem.operators[0]);
}
});
it("distributes the multiplier correctly for both operators", () => {
for (let index = 0; index < 100; index += 1) {
const problem = generateProblem();
const signedB = problem.operator === "-" ? -problem.b : problem.b;
const signedTerms = problem.terms.map((term, termIndex) => {
if (termIndex === 0) return term;
expect(problem.distributedA).toBe(problem.multiplier * problem.a);
expect(problem.distributedB).toBe(problem.multiplier * signedB);
return problem.operators[termIndex - 1] === "-" ? -term : term;
});
expect(problem.distributedTerms).toEqual(
signedTerms.map((term) => problem.multiplier * term),
);
}
});
@@ -37,20 +41,15 @@ describe("parenthesis sign problem engine", () => {
const problem = generateProblem();
const correctResult = gradeAnswer(problem, {
a: problem.distributedA,
b: problem.distributedB,
terms: problem.distributedTerms,
});
expect(correctResult.correct).toBe(true);
expect(correctResult.userAnswer).toEqual([
problem.distributedA,
problem.distributedB,
]);
expect(correctResult.userAnswer).toEqual(problem.distributedTerms);
expect(
gradeAnswer(problem, {
a: problem.distributedB,
b: problem.distributedA,
terms: [...problem.distributedTerms].reverse(),
}).correct,
).toBe(false);
});
+8 -31
View File
@@ -3,6 +3,7 @@ import type {
ParenthesisSignProblem,
UserAnswer,
} from "./types";
import { formatParenthesisExpressionLatex } from "./parenthesis-latex";
const MIN_MULTIPLIER = -9;
const MAX_MULTIPLIER = -1;
@@ -33,19 +34,6 @@ function formatLatexTerms(terms: number[]) {
.join(" ");
}
function formatParenthesisTerms(
terms: number[],
operators: ("+" | "-")[],
) {
return terms
.map((term, index) => {
if (index === 0) return `${term}`;
return `${operators[index - 1]} ${term}`;
})
.join(" ");
}
function createProblem({
multiplier,
terms,
@@ -57,9 +45,6 @@ function createProblem({
return operators[index - 1] === "-" ? -term : term;
});
const distributedTerms = signedTerms.map((term) => multiplier * term);
const [a, b] = terms;
const [operator] = operators;
const [distributedA, distributedB] = distributedTerms;
const key = [
multiplier,
...terms.flatMap((term, index) =>
@@ -73,16 +58,13 @@ function createProblem({
multiplier,
terms,
operators,
a,
operator,
b,
distributedTerms,
distributedA,
distributedB,
latexBefore: `${multiplier}\\left(${formatParenthesisTerms(
latexBefore: formatParenthesisExpressionLatex({
multiplier,
terms,
operators,
)}\\right)`,
formatTermLatex: (term) => `${term}`,
}),
latexAfter: formatLatexTerms(distributedTerms),
};
}
@@ -222,16 +204,11 @@ export function gradeAnswer(
problem: ParenthesisSignProblem,
answer: UserAnswer,
): GradeResult {
const userAnswer =
answer.terms ?? (answer.a !== undefined && answer.b !== undefined
? [answer.a, answer.b]
: []);
return {
correct:
userAnswer.length === problem.distributedTerms.length &&
userAnswer.every((value, index) => value === problem.distributedTerms[index]),
userAnswer,
answer.terms.length === problem.distributedTerms.length &&
answer.terms.every((value, index) => value === problem.distributedTerms[index]),
userAnswer: answer.terms,
correctAnswer: problem.distributedTerms,
};
}
+1 -8
View File
@@ -4,20 +4,13 @@ export interface ParenthesisSignProblem {
multiplier: number;
terms: number[];
operators: ("+" | "-")[];
a: number;
operator: "+" | "-";
b: number;
distributedTerms: number[];
distributedA: number;
distributedB: number;
latexBefore: string;
latexAfter: string;
}
export interface UserAnswer {
terms?: number[];
a?: number;
b?: number;
terms: number[];
}
export interface GradeResult {
+10 -11
View File
@@ -25,27 +25,26 @@ function findCorrectChoiceIds(problem: VariableParenthesisProblem) {
describe("variable parenthesis engine", () => {
it("generates a problem with exactly one variable term inside parentheses", () => {
const problem = generateVariableParenthesisProblem();
const terms = [problem.firstTerm, problem.secondTerm];
expect(terms.filter((term) => term.kind === "variable")).toHaveLength(1);
expect(terms.filter((term) => term.kind === "constant")).toHaveLength(1);
expect(problem.terms.filter((term) => term.kind === "variable")).toHaveLength(1);
expect(problem.terms.filter((term) => term.kind === "constant")).toHaveLength(1);
expect(problem.multiplier).toBeLessThan(0);
expect(Number.isInteger(problem.multiplier)).toBe(true);
expect(problem.firstTerm.coefficient).toBeGreaterThanOrEqual(1);
expect(problem.secondTerm.coefficient).toBeGreaterThanOrEqual(1);
expect(
problem.terms.every((term) => term.coefficient >= 1 && term.coefficient <= 9),
).toBe(true);
});
it("distributes signs correctly for addition and subtraction", () => {
const problems = generateVariableParenthesisSession(50);
for (const problem of problems) {
const secondSign = problem.operator === "-" ? -1 : 1;
expect(problem.correctTerms.map((term) => term.coefficient)).toEqual(
problem.terms.map((term, index) => {
const sign = index === 0 || problem.operators[index - 1] === "+" ? 1 : -1;
expect(problem.correctTerms[0].coefficient).toBe(
problem.multiplier * problem.firstTerm.coefficient,
);
expect(problem.correctTerms[1].coefficient).toBe(
problem.multiplier * secondSign * problem.secondTerm.coefficient,
return problem.multiplier * sign * term.coefficient;
}),
);
}
});
+7 -16
View File
@@ -6,6 +6,7 @@ import type {
VariableUserAnswer,
VariableGradeResult,
} from "./variable-types";
import { formatParenthesisExpressionLatex } from "./parenthesis-latex";
const MIN_MULTIPLIER = -9;
const MAX_MULTIPLIER = -1;
@@ -95,10 +96,6 @@ export function formatVariableTermLatex(
return `${term.coefficient}x`;
}
function formatParenthesisTermLatex(term: VariableParenthesisTerm) {
return formatVariableTermLatex(term);
}
export function formatVariableExpressionLatex(
terms: DistributedVariableTerm[],
) {
@@ -164,8 +161,6 @@ function createChoices(
function createProblem(seed: VariableProblemSeed): VariableParenthesisProblem {
const { multiplier, terms, operators } = seed;
const [firstTerm, secondTerm] = terms;
const [operator] = operators;
const key = [
multiplier,
...terms.flatMap((term, index) =>
@@ -189,18 +184,14 @@ function createProblem(seed: VariableProblemSeed): VariableParenthesisProblem {
multiplier,
terms,
operators,
operator,
firstTerm,
secondTerm,
correctTerms,
choices,
latexBefore: `${multiplier}\\left(${terms
.map((term, index) => {
if (index === 0) return formatParenthesisTermLatex(term);
return `${operators[index - 1]} ${formatParenthesisTermLatex(term)}`;
})
.join(" ")}\\right)`,
latexBefore: formatParenthesisExpressionLatex({
multiplier,
terms,
operators,
formatTermLatex: formatVariableTermLatex,
}),
latexAfter: formatVariableExpressionLatex(correctTerms),
};
}
-3
View File
@@ -26,9 +26,6 @@ export interface VariableParenthesisProblem {
multiplier: number;
terms: VariableParenthesisTerm[];
operators: ("+" | "-")[];
operator: "+" | "-";
firstTerm: VariableParenthesisTerm;
secondTerm: VariableParenthesisTerm;
correctTerms: DistributedVariableTerm[];
choices: VariableTermChoice[][];
latexBefore: string;