{ }codelper
相等比较与类型转换空值 nullP0124

判断相同的四种方法

第一行是 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()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 2 种语言题解

本题只接受 JavaScript 和 TypeScript,因为这两种语言判断相同的方法多达四种:==、===、Object.is 和 Set(Python 的 ==、is,Java 的 ==、equals,C# 的 ==、Equals 都不会这样转换类型,Craykon 没有这类比较)。
首先要把片段转换成值。
保持字符串的话,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"));