feat(m02-df): 끊긴 곳 무게 — 쉼(쉬는 갈래) · 문제
- CfBreak.level: 통과 안 됨 · 그 아래로 번진 빈 값 = 쉼 · 빈 받는 자리 · 표에 없는 조건 · 맞는 재료 없음 · 조건 겹침 · 안 맞음 · 오류 = 문제 - 쉬는 갈래에서 온 빈 값은 받는 상자도 쉼(셈 안 함 · 출력 빈 값 · 문제로 안 셈) - M02_DF_Compute.py breaks 에 같은 뜻 level 한 칸 - 시험 25개 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VBToTCGk5dqDKeXCEP3vhk
This commit is contained in:
@@ -470,6 +470,8 @@ def run(lg: dict, values: dict, load: Loader, local: list[dict], stack: list | N
|
||||
breaks.append(
|
||||
{
|
||||
"kind": kind,
|
||||
# 무게 — 통과 안 됨 = 쉼(조건이 안 맞아 쉬는 갈래) · 나머지 = 문제(Eval CfBreak.level 과 같음)
|
||||
"level": "쉼" if kind == "통과 안 됨" else "문제",
|
||||
"box": to.get("box"),
|
||||
"port": to.get("name"),
|
||||
"link": ln.get("id"),
|
||||
|
||||
@@ -43,6 +43,8 @@ export interface CfBreak {
|
||||
| "조건 겹침"
|
||||
| "조건 안 맞음"
|
||||
| "오류";
|
||||
/** 무게 — 「쉼」 = 조건이 안 맞아 쉬는 갈래(그 아래로 번진 빈 값 포함) · 「문제」 = 고쳐야 할 끊김 */
|
||||
level: "문제" | "쉼";
|
||||
box: string;
|
||||
port?: string;
|
||||
link?: string;
|
||||
@@ -112,6 +114,11 @@ const TEXT = {
|
||||
"「{from}」 → 「{box}」 선 — 표에 맞는 줄이 여럿",
|
||||
"Line 「{from}」 → 「{box}」 — several table rows match",
|
||||
],
|
||||
/** {from} {box} */
|
||||
Rest: [
|
||||
"「{from}」 → 「{box}」 — 앞 조건이 안 맞아 쉬는 갈래",
|
||||
"「{from}」 → 「{box}」 — resting branch (an earlier condition doesn't match)",
|
||||
],
|
||||
RuleError: [
|
||||
"「{from}」 → 「{box}」 선 규칙을 셀 수 없음",
|
||||
"Line 「{from}」 → 「{box}」 rule can't be worked out",
|
||||
@@ -251,9 +258,26 @@ export function evalLogic(
|
||||
const passed = new Map<string, boolean>();
|
||||
/** 선 규칙에서 막힌 상자 — 그 함수는 셈하지 않고 빈 값을 넘김(갈래가 끊김) */
|
||||
const blocked = new Set<string>();
|
||||
/** 조건이 안 맞아 쉬는 상자 — 그 아래로 번짐 */
|
||||
const resting = new Set<string>();
|
||||
/** 선 하나로 들어오는 값 — 규칙이 있으면 엔진으로 */
|
||||
const through = (l: CfLink): CfValue => {
|
||||
const v = valueOf(l.from.box, l.from.name);
|
||||
// 쉬는 갈래에서 온 빈 값 — 받는 상자도 쉼(셈하지 않음 · 문제로 안 셈)
|
||||
if (v === null && resting.has(l.from.box)) {
|
||||
resting.add(l.to.box);
|
||||
blocked.add(l.to.box);
|
||||
const text = tl("Rest", { from: name(l.from.box), box: name(l.to.box) });
|
||||
found.push({
|
||||
kind: "통과 안 됨",
|
||||
level: "쉼",
|
||||
box: l.to.box,
|
||||
port: l.to.name,
|
||||
link: l.id,
|
||||
text,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
if (!l.rule) return v;
|
||||
if (v === null && (l.rule.kind === "scale" || l.rule.kind === "round")) return null;
|
||||
const conds = Object.fromEntries(
|
||||
@@ -262,15 +286,17 @@ export function evalLogic(
|
||||
const res = applyRule(l.rule, v, conds);
|
||||
if (l.rule.kind === "gate") passed.set(l.id, res.why === null);
|
||||
const fill = { from: name(l.from.box), box: name(l.to.box), rule: ruleLabel(l.rule) };
|
||||
if (res.why === "통과 안 됨")
|
||||
if (res.why === "통과 안 됨") {
|
||||
resting.add(l.to.box);
|
||||
found.push({
|
||||
kind: "통과 안 됨",
|
||||
level: "쉼",
|
||||
box: l.to.box,
|
||||
port: l.to.name,
|
||||
link: l.id,
|
||||
text: tl("NotPassed", fill),
|
||||
});
|
||||
else if (res.why) {
|
||||
} else if (res.why) {
|
||||
const kind = res.why === "표에 없는 조건" ? "표에 없는 조건" : "오류";
|
||||
const key =
|
||||
res.why === "표에 없는 조건"
|
||||
@@ -279,7 +305,7 @@ export function evalLogic(
|
||||
? "ManyRows"
|
||||
: "RuleError";
|
||||
const text = tl(key, fill);
|
||||
found.push({ kind, box: l.to.box, port: l.to.name, link: l.id, text });
|
||||
found.push({ kind, level: "문제", box: l.to.box, port: l.to.name, link: l.id, text });
|
||||
errors.push(text);
|
||||
}
|
||||
if (res.why) blocked.add(l.to.box);
|
||||
@@ -303,7 +329,13 @@ export function evalLogic(
|
||||
for (const e of b.input?.elements ?? [])
|
||||
if (e.mode === "rule" && ports[b.id][elementPort(e)] === null) {
|
||||
const text = tl("NoMaterial", { box: b.name, pill: elementPort(e) });
|
||||
found.push({ kind: "맞는 재료 없음", box: b.id, port: elementPort(e), text });
|
||||
found.push({
|
||||
kind: "맞는 재료 없음",
|
||||
level: "문제",
|
||||
box: b.id,
|
||||
port: elementPort(e),
|
||||
text,
|
||||
});
|
||||
errors.push(text);
|
||||
}
|
||||
} else if (b.kind === "input") ports[b.id] = otherLogic(b, arrive(b), given, opts, errors);
|
||||
@@ -383,6 +415,7 @@ function gates(
|
||||
const text = tl(n > 1 ? "Overlap" : "NoneMatch", fill);
|
||||
found.push({
|
||||
kind: n > 1 ? "조건 겹침" : "조건 안 맞음",
|
||||
level: "문제",
|
||||
box: g.from.box,
|
||||
port: g.from.name,
|
||||
text,
|
||||
@@ -402,6 +435,7 @@ export function breaks(lg: CfLogic, values: CfValues = {}, opts: CfEvalOptions =
|
||||
if (!lg.links.some((l) => l.to.box === b.id && l.to.name === p.name))
|
||||
out.push({
|
||||
kind: "빈 받는 자리",
|
||||
level: "문제",
|
||||
box: b.id,
|
||||
port: p.name,
|
||||
text: tl("EmptyIn", { box: b.name, pill: p.name }),
|
||||
|
||||
@@ -450,16 +450,17 @@ def test_나_조건_통과로_둘로_가르기_표에서_찾기() -> None:
|
||||
a_out, a_breaks, b_out, b_breaks, far = got
|
||||
# 토사: 0.5 × 100000 × 1.1 · 암 줄은 통과 안 됨 · 높이 4.5 → 증가율 40
|
||||
assert a_out == {"b6": 55000, "b7": None, "b8": 40}
|
||||
assert a_breaks == [["통과 안 됨", "l2"]]
|
||||
assert a_breaks == [["통과 안 됨", "l2"], ["통과 안 됨", "l5"]] # 암 갈래 + 그 아래로 번진 쉼
|
||||
assert b_out == {
|
||||
"b6": None,
|
||||
"b7": 50000,
|
||||
"b8": 30,
|
||||
} # 암 50,000 → 천 원 버림 그대로 · 높이 3.5 → 30
|
||||
assert b_breaks == ["통과 안 됨"]
|
||||
assert b_breaks == ["통과 안 됨", "통과 안 됨"] # 토사 갈래 + 번진 쉼
|
||||
assert far == [
|
||||
["통과 안 됨", "「입력」 → 「함수 2」 선이 조건(지반 = 암 일 때만)을 통과하지 않음"],
|
||||
["표에 없는 조건", "「입력 2」 → 「함수 3」 선 — 표에 맞는 줄이 없음"],
|
||||
["통과 안 됨", "「함수 2」 → 「출력 2」 — 앞 조건이 안 맞아 쉬는 갈래"],
|
||||
]
|
||||
|
||||
|
||||
@@ -471,7 +472,7 @@ def test_나_조건_선_겹침_안맞음() -> None:
|
||||
)
|
||||
overlap, none = got
|
||||
assert overlap == [["조건 겹침", "「입력」 의 「지반」 에서 나간 조건 선이 둘 이상 통과함"]]
|
||||
assert none == ["통과 안 됨", "통과 안 됨", "조건 안 맞음"]
|
||||
assert none == ["통과 안 됨", "통과 안 됨", "통과 안 됨", "통과 안 됨", "조건 안 맞음"]
|
||||
|
||||
|
||||
def test_나_규칙_글과_엑셀_수식() -> None:
|
||||
@@ -620,3 +621,25 @@ def test_표_범위칸_숫자하나는_같음() -> None:
|
||||
)
|
||||
# 4 = 「4」 줄만(같음) · 4 는 「4~6」(초과) 에 안 듦 · 빈 아래 끝은 열림
|
||||
assert got == [[0.5, None], [0.6, None], [0.4, None], [None, "표에 없는 조건"], [0.6, None]]
|
||||
|
||||
|
||||
def test_쉬는_갈래와_문제_무게() -> None:
|
||||
got = _run(
|
||||
SPLIT + "const a = E.breaks(lg, V('토사'));\n"
|
||||
"const b = E.breaks(lg, V('자갈', 9));\n"
|
||||
"out([a.map(x => [x.kind, x.level, x.box, x.text]), b.filter(x => x.level === '문제').map(x => x.kind),"
|
||||
" E.evalLogic(lg, V('토사')).outputs]);"
|
||||
)
|
||||
a, b_problems, outs = got
|
||||
assert a == [
|
||||
[
|
||||
"통과 안 됨",
|
||||
"쉼",
|
||||
"b4",
|
||||
"「입력」 → 「함수 2」 선이 조건(지반 = 암 일 때만)을 통과하지 않음",
|
||||
],
|
||||
["통과 안 됨", "쉼", "b7", "「함수 2」 → 「출력 2」 — 앞 조건이 안 맞아 쉬는 갈래"],
|
||||
]
|
||||
# 하나도 안 맞음 · 표에 없는 조건만 문제 — 쉬는 갈래 넷은 문제로 안 셈
|
||||
assert b_problems == ["표에 없는 조건", "조건 안 맞음"]
|
||||
assert outs["b7"] is None # 쉬는 갈래 끝 출력은 빈 값(0 아님)
|
||||
|
||||
Reference in New Issue
Block a user