{ }codelper
같음 비교와 형 변환빈 값(null)P0124

같음을 재는 네 가지 방법

첫 줄에 n이, 둘째 줄에 값 n개가 빈칸 하나로 띄워 주어진다.
각 값은 JavaScript 코드에 그대로 쓸 수 있는 문자열 토막이다. 토막은 정수(예: 5, -12, -0), 큰따옴표로 감싼 문자열(예: "5", "", "abc"), 또는 true, false, null, undefined, NaN 가운데 하나다. 토막을 그 JavaScript 값으로 바꾼 뒤, 같은 값을 걸러 내는 일을 네 가지 같음 규칙으로 따로따로 하라. 규칙은 차례로 a == b, a === b, Object.is(a, b), 그리고 Set이 쓰는 같음(new Set에 넣었을 때 하나로 합쳐지는가)이다. 값을 앞에서부터 하나씩 보면서, 그때까지 남긴 값 가운데 그 규칙으로 같은 것이 하나도 없으면 남기고, 있으면 버린다. 규칙마다 한 줄에 남긴 값의 개수와, 남긴 값들의 자리(1부터 센 입력 차례)를 작은 것부터 빈칸으로 띄워 출력하라.

입력
1 ≤ n ≤ 2000.
정수는 -1000 이상 1000 이하이고 앞에 쓸데없는 0이 붙지 않는다(-0은 나올 수 있다).
따옴표 안의 문자열은 비었거나, 숫자 1 ~ 4글자(0으로 시작할 수 있다)이거나, 영어 소문자 1 ~ 5글자다.
출력
네 줄(==, ===, Object.is, Set 차례)
예시 1 · stdin
8
0 -0 NaN NaN "0" null undefined false
stdout
4 1 3 4 6
7 1 3 4 5 6 7 8
7 1 2 3 5 6 7 8
6 1 3 5 6 7 8
예시 2 · stdin
3
"" 0 "0"
stdout
2 1 3
3 1 2 3
3 1 2 3
3 1 2 3
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 2개 언어 풀이

이 문제는 같음을 재는 방법이 넷(==·===·Object.is·Set)이나 있는 JavaScript와 TypeScript만 받아요(Python ==·is, Java ==·equals, C# ==·Equals는 다른 타입을 이렇게 바꾸지 않고, 크레이콘에는 이런 비교가 없어요).
먼저 토막을 값으로 바꿔야 해요.
문자열 그대로 두면 0과 -0, 5와 "5"가 그냥 서로 다른 문자열이에요.
네 규칙은 NaN과 0/-0에서 갈려요.
===에서는 NaN이 자기와도 달라서 모든 NaN이 남아요.
Object.is는 NaN끼리 같다고 보지만 0과 -0은 가르고, Set·Map·includes가 쓰는 SameValueZero는 NaN끼리도, 0과 -0도 같다고 봐요.
==는 null과 undefined를 서로끼리만 같다고 보고(null == 0, null == false는 거짓), 이어지지도 않아요("" == 0이고 0 == "0"인데 "" == "0"은 거짓).
그래서 == 줄은 어느 값을 먼저 남겼는지에 따라 달라져요.
함정 셋: ① indexOf는 ===로 찾아서 NaN을 못 찾고, includes는 SameValueZero라 찾아요.
그래서 indexOf로 Set을 흉내 내면 NaN이 거듭 남고, includes로 Object.is를 대신하면 -0이 0에 묻혀요.
② 보통 객체 {}를 집합처럼 쓰면 열쇠가 문자열로 바뀌어 5와 "5", null과 "null"이 같은 열쇠가 돼요.
③ JSON.stringify를 열쇠로 쓰면 NaN이 "null"이 되어 null과 섞이고, -0도 "0"이 돼요.
TypeScript는 string과 number로 좁혀진 두 값을 ==로 비교하면 TS2367 오류를 내서, 합친 타입(string | number | boolean | null | undefined)으로 받아요.
실행 결과는 JavaScript와 똑같아요.

JavaScriptmain.js · 41 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n"); // 문자열 토막 하나를 JavaScript 값으로 바꾼다function parse(token) {  if (token.startsWith('"')) return token.slice(1, -1); // "5" → 문자열 5  if (token === "true") return true;  if (token === "false") return false;  if (token === "null") return null;  if (token === "undefined") return undefined;  if (token === "NaN") return NaN;  return Number(token); // -0 도 그대로 -0} const values = lines[1].trim().split(" ").map(parse); // 앞에서부터 보며, 이미 남긴 값 가운데 같은 것이 없으면 남긴다. 남긴 자리(1부터)를 돌려준다function keepFirst(same) {  const kept = [];  values.forEach((v, i) => {    if (!kept.some((j) => same(values[j], v))) kept.push(i);  });  return kept.map((i) => i + 1);} // Set은 SameValueZero: NaN끼리 같고, 0과 -0도 같다function keepFirstBySet() {  const seen = new Set();  const kept = [];  values.forEach((v, i) => {    if (!seen.has(v)) { seen.add(v); kept.push(i + 1); }  });  return kept;} const results = [  keepFirst((a, b) => a == b),  keepFirst((a, b) => a === b),  keepFirst(Object.is), // NaN끼리 같고, 0과 -0은 다르다  keepFirstBySet(),];console.log(results.map((kept) => [kept.length, ...kept].join(" ")).join("\n"));
TypeScriptmain.ts · 45 줄
import { readFileSync } from "node:fs"; type Value = string | number | boolean | null | undefined; // 문자열 토막 하나를 JavaScript 값으로 바꾼다function parse(token: string): Value {  if (token.startsWith('"')) return token.slice(1, -1);  if (token === "true") return true;  if (token === "false") return false;  if (token === "null") return null;  if (token === "undefined") return undefined;  if (token === "NaN") return NaN;  return Number(token); // -0 도 그대로 -0} const lines: string[] = readFileSync(0, "utf8").split("\n");const values: Value[] = lines[1].trim().split(" ").map(parse); // 앞에서부터 보며, 이미 남긴 값 가운데 같은 것이 없으면 남긴다. 남긴 자리(1부터)를 돌려준다function keepFirst(same: (a: Value, b: Value) => boolean): number[] {  const kept: number[] = [];  values.forEach((v, i) => {    if (!kept.some((j) => same(values[j], v))) kept.push(i);  });  return kept.map((i) => i + 1);} // Set은 SameValueZero: NaN끼리 같고, 0과 -0도 같다function keepFirstBySet(): number[] {  const seen = new Set<Value>();  const kept: number[] = [];  values.forEach((v, i) => {    if (!seen.has(v)) { seen.add(v); kept.push(i + 1); }  });  return kept;} // 매개변수가 합친 타입(Value)이라 TS도 == 를 허락한다const results: number[][] = [  keepFirst((a, b) => a == b),  keepFirst((a, b) => a === b),  keepFirst(Object.is), // NaN끼리 같고, 0과 -0은 다르다  keepFirstBySet(),];console.log(results.map((kept) => [kept.length, ...kept].join(" ")).join("\n"));