리펙토링: 코드 최적화
This commit is contained in:
@@ -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)`;
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -26,9 +26,6 @@ export interface VariableParenthesisProblem {
|
||||
multiplier: number;
|
||||
terms: VariableParenthesisTerm[];
|
||||
operators: ("+" | "-")[];
|
||||
operator: "+" | "-";
|
||||
firstTerm: VariableParenthesisTerm;
|
||||
secondTerm: VariableParenthesisTerm;
|
||||
correctTerms: DistributedVariableTerm[];
|
||||
choices: VariableTermChoice[][];
|
||||
latexBefore: string;
|
||||
|
||||
Reference in New Issue
Block a user