3 3 0 1
0 0 0 0
error twice w
error twice w
error order
error missing w
error many
error unknown dept
error twice depth
9 0 0 0
样例 2 · stdin
3
base 10
low base
high low
4
call
call 4
call low=0
call high=1 base=2
stdout
10 10 10
4 4 4
10 0 0
2 2 1
main.py
错误列表00F8 ↓ · ⇧F8 ↑
输入(stdin)
运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。
$ 就绪
请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。
其他语言怎么写?查看各语言题解 · 8 种语言题解
这道题要亲手实现 Python 和 C# 里的命名实参和默认值规则。 真实的语言里是这样的。 Python 把 def box(w, h=None, depth=0, *, pad) 写成 box(3, pad=1) 来调用。 box(5, w=0) 是 TypeError(got multiple values for argument 'w'),不存在的名字 dept=1 也是 TypeError,而 box(pad=2, 4) 在运行之前就是 SyntaxError(positional argument follows keyword argument)。 C# 写作 Box(3, pad: 1),Box(5, w: 0) 是编译错误 CS1744,Box(h: 2, 4) 是 CS8323(像 Box(w: 1, 2) 这样命名实参在自己本来的位置上时,后面可以跟位置实参)。 JS 和 TS 没有命名实参,所以传一个对象,再用 function box({ w, h = w, depth = 0, pad }) 拆开接收。 在 JS 里 box({ w: 1, w: 2 }) 不报错,后面的值生效,不认识的键 dept 会被悄悄丢掉。 Java、C++、C 和 Craykon 没有命名实参。 C 用结构体来模仿,写成 box((struct Box){ .w = 3, .pad = 1 }),但没写的成员会变成 0,所以分不清“没给”和“给了 0”。 默认值也不一样。 C++ 的 int h = 0 只能写在靠后的形参上,int h = w 是编译错误。 Python 的 def g(w, h=w) 也是 NameError,所以用 h=None 接收,再用 if h is None: h = w 填上。 JS 和 TS 可以写 function pos(w, h = w),每次调用时都重新计算。 Java 和 Craykon 没有默认值:Java 声明几个同名函数,Craykon 在值的个数不对时停止。 陷阱一:给的值可能是 0。 如果像 Python if values[i]: 那样用值的真假来问“有没有值”,放了 0 的位置看起来是空的,call 0 w=5 就不报错地通过了。 要用 Python is None、JS 和 TS === null、C# int? 的 HasValue、Java Integer 的 null、C++ std::optional 的 has_value()、C 里另外准备的 bool 数组、Craykon 的 != 空 来问。 Python h = h or w、JS h = h || w 是同一个陷阱,给的 0 会被换成默认值。 陷阱二:默认值 0 也是默认值。 用 JS if (defaultValue[i]) 来问,depth 0 就成了没有默认值的形参。 陷阱三:h w 的默认值是 w 在这次调用中得到的值。 如果读声明时就把前面形参的默认值抄过来,第二个样例的 call 4 会变成 4 10 10。 从前面的形参开始依次填就可以了。 陷阱四:错误是从左到右看实参时最先遇到的那一个。 按错误种类分别扫描,call pad=2 4 nope=1 会输出 unknown 而不是 order。 missing 也是声明顺序里最靠前的那个,所以 call h=7 报的是 w 而不是 pad。 陷阱五:名字必须整个相同。 像 C strncmp(names[i], name, strlen(name)) 那样只比较前面一部分,dept 就会被当成 depth。
Pythonmain.py · 64 行
import sysdef bind(names, default_value, default_from, args): """인자를 매개변수에 맞춰 넣는다. (값 목록, "") 또는 (None, 오류)를 돌려준다.""" p = len(names) values = [None] * p # None = 아직 값이 들어가지 않음(0도 들어간 값이다) positional = 0 seen_named = False for arg in args: if "=" in arg: seen_named = True name, text = arg.split("=") if name not in names: return None, "unknown " + name i = names.index(name) if values[i] is not None: return None, "twice " + name values[i] = int(text) else: if seen_named: return None, "order" if positional == p: return None, "many" values[positional] = int(arg) positional += 1 for i in range(p): # 앞에서부터 채워야 앞 매개변수의 값을 기본값으로 쓸 수 있다 if values[i] is None: if default_from[i] >= 0: values[i] = values[default_from[i]] elif default_value[i] is not None: values[i] = default_value[i] else: return None, "missing " + names[i] return values, ""def main(): lines = sys.stdin.read().split("\n") p = int(lines[0]) names, default_value, default_from = [], [], [] for line in lines[1:1 + p]: parts = line.split() value, source = None, -1 if len(parts) == 2: if parts[1][0].isalpha(): source = names.index(parts[1]) else: value = int(parts[1]) names.append(parts[0]) default_value.append(value) default_from.append(source) q = int(lines[1 + p]) out = [] for line in lines[2 + p:2 + p + q]: values, error = bind(names, default_value, default_from, line.split()[1:]) if values is None: out.append("error " + error) else: out.append(" ".join(map(str, values))) print("\n".join(out))main()
JavaScriptmain.js · 56 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");// 인자를 매개변수에 맞춰 넣는다. 값 배열 또는 오류 문자열을 돌려준다.function bind(names, defaultValue, defaultFrom, args) { const p = names.length; const values = new Array(p).fill(null); // null = 아직 값이 들어가지 않음(0도 들어간 값이다) let positional = 0; let seenNamed = false; for (const arg of args) { const eq = arg.indexOf("="); if (eq >= 0) { seenNamed = true; const name = arg.slice(0, eq); const i = names.indexOf(name); if (i < 0) return "unknown " + name; if (values[i] !== null) return "twice " + name; values[i] = Number(arg.slice(eq + 1)); } else { if (seenNamed) return "order"; if (positional === p) return "many"; values[positional++] = Number(arg); } } for (let i = 0; i < p; i++) { if (values[i] !== null) continue; if (defaultFrom[i] >= 0) values[i] = values[defaultFrom[i]]; else if (defaultValue[i] !== null) values[i] = defaultValue[i]; else return "missing " + names[i]; } return values;}const p = Number(lines[0]);const names = [];const defaultValue = [];const defaultFrom = [];for (let i = 1; i <= p; i++) { const parts = lines[i].trim().split(" "); let value = null; let source = -1; if (parts.length === 2) { if (/^[a-z]/.test(parts[1])) source = names.indexOf(parts[1]); else value = Number(parts[1]); } names.push(parts[0]); defaultValue.push(value); defaultFrom.push(source);}const q = Number(lines[p + 1]);const out = [];for (let i = 0; i < q; i++) { const args = lines[p + 2 + i].trim().split(" ").slice(1); const result = bind(names, defaultValue, defaultFrom, args); out.push(typeof result === "string" ? "error " + result : result.join(" "));}console.log(out.join("\n"));
TypeScriptmain.ts · 62 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");// 인자를 매개변수에 맞춰 넣는다. 값 배열 또는 오류 문자열을 돌려준다.function bind(names: string[], defaultValue: (number | null)[], defaultFrom: number[], args: string[]): number[] | string { const p = names.length; const values: (number | null)[] = new Array<number | null>(p).fill(null); // null = 아직 값이 들어가지 않음 let positional = 0; let seenNamed = false; for (const arg of args) { const eq = arg.indexOf("="); if (eq >= 0) { seenNamed = true; const name = arg.slice(0, eq); const i = names.indexOf(name); if (i < 0) return "unknown " + name; if (values[i] !== null) return "twice " + name; values[i] = Number(arg.slice(eq + 1)); } else { if (seenNamed) return "order"; if (positional === p) return "many"; values[positional++] = Number(arg); } } const filled: number[] = []; for (let i = 0; i < p; i++) { let v: number | null = values[i]; if (v === null) { if (defaultFrom[i] >= 0) v = filled[defaultFrom[i]]; else v = defaultValue[i]; } if (v === null) return "missing " + names[i]; filled.push(v); } return filled;}const p: number = Number(lines[0]);const names: string[] = [];const defaultValue: (number | null)[] = [];const defaultFrom: number[] = [];for (let i = 1; i <= p; i++) { const parts: string[] = lines[i].trim().split(" "); let value: number | null = null; let source = -1; if (parts.length === 2) { if (/^[a-z]/.test(parts[1])) source = names.indexOf(parts[1]); else value = Number(parts[1]); } names.push(parts[0]); defaultValue.push(value); defaultFrom.push(source);}const q: number = Number(lines[p + 1]);const out: string[] = [];for (let i = 0; i < q; i++) { const args: string[] = lines[p + 2 + i].trim().split(" ").slice(1); const result = bind(names, defaultValue, defaultFrom, args); out.push(typeof result === "string" ? "error " + result : result.join(" "));}console.log(out.join("\n"));
JavaMain.java · 72 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.List;public class Main { static List<String> names = new ArrayList<>(); static List<Integer> defaultValue = new ArrayList<>(); // null = 기본값이 정수가 아님 static List<Integer> defaultFrom = new ArrayList<>(); // -1 = 앞 매개변수를 쓰지 않음 // 인자를 매개변수에 맞춰 넣는다. 오류가 없으면 values를 채우고 null을, 있으면 오류 문자열을 돌려준다. static String bind(String[] args, Integer[] values) { int p = names.size(); int positional = 0; boolean seenNamed = false; for (int a = 1; a < args.length; a++) { String arg = args[a]; int eq = arg.indexOf('='); if (eq >= 0) { seenNamed = true; String name = arg.substring(0, eq); int i = names.indexOf(name); if (i < 0) return "unknown " + name; if (values[i] != null) return "twice " + name; // null = 아직 값이 들어가지 않음 values[i] = Integer.parseInt(arg.substring(eq + 1)); } else { if (seenNamed) return "order"; if (positional == p) return "many"; values[positional++] = Integer.parseInt(arg); } } for (int i = 0; i < p; i++) { if (values[i] != null) continue; if (defaultFrom.get(i) >= 0) values[i] = values[defaultFrom.get(i)]; else if (defaultValue.get(i) != null) values[i] = defaultValue.get(i); else return "missing " + names.get(i); } return null; } public static void main(String[] argv) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int p = Integer.parseInt(in.readLine().trim()); for (int i = 0; i < p; i++) { String[] parts = in.readLine().trim().split(" "); Integer value = null; int source = -1; if (parts.length == 2) { if (Character.isLetter(parts[1].charAt(0))) source = names.indexOf(parts[1]); else value = Integer.parseInt(parts[1]); } names.add(parts[0]); defaultValue.add(value); defaultFrom.add(source); } int q = Integer.parseInt(in.readLine().trim()); StringBuilder out = new StringBuilder(); for (int t = 0; t < q; t++) { String[] args = in.readLine().trim().split(" "); Integer[] values = new Integer[p]; String error = bind(args, values); if (error != null) { out.append("error ").append(error).append('\n'); } else { for (int i = 0; i < p; i++) out.append(i > 0 ? " " : "").append(values[i]); out.append('\n'); } } System.out.print(out); }}
C#Main.cs · 76 行
using System;using System.Collections.Generic;using System.Text;class Program{ static List<string> names = new List<string>(); static List<int?> defaultValue = new List<int?>(); // null = 기본값이 정수가 아님 static List<int> defaultFrom = new List<int>(); // -1 = 앞 매개변수를 쓰지 않음 // 인자를 매개변수에 맞춰 넣는다. 오류가 없으면 values를 채우고 null을, 있으면 오류 문자열을 돌려준다. static string Bind(string[] args, int?[] values) { int p = names.Count; int positional = 0; bool seenNamed = false; for (int a = 1; a < args.Length; a++) { string arg = args[a]; int eq = arg.IndexOf('='); if (eq >= 0) { seenNamed = true; string name = arg.Substring(0, eq); int i = names.IndexOf(name); if (i < 0) return "unknown " + name; if (values[i].HasValue) return "twice " + name; // 0도 들어간 값이다 values[i] = int.Parse(arg.Substring(eq + 1)); } else { if (seenNamed) return "order"; if (positional == p) return "many"; values[positional++] = int.Parse(arg); } } for (int i = 0; i < p; i++) { if (values[i].HasValue) continue; if (defaultFrom[i] >= 0) values[i] = values[defaultFrom[i]]; else if (defaultValue[i].HasValue) values[i] = defaultValue[i]; else return "missing " + names[i]; } return null; } static void Main() { int p = int.Parse(Console.ReadLine().Trim()); for (int i = 0; i < p; i++) { string[] parts = Console.ReadLine().Trim().Split(' '); int? value = null; int source = -1; if (parts.Length == 2) { if (char.IsLetter(parts[1][0])) source = names.IndexOf(parts[1]); else value = int.Parse(parts[1]); } names.Add(parts[0]); defaultValue.Add(value); defaultFrom.Add(source); } int q = int.Parse(Console.ReadLine().Trim()); var sb = new StringBuilder(); for (int t = 0; t < q; t++) { string[] args = Console.ReadLine().Trim().Split(' '); var values = new int?[p]; string error = Bind(args, values); if (error != null) sb.Append("error ").Append(error).Append('\n'); else sb.Append(string.Join(" ", values)).Append('\n'); } Console.Write(sb); }}
C++main.cpp · 88 行
#include <algorithm>#include <cctype>#include <iostream>#include <optional>#include <sstream>#include <string>#include <vector>using namespace std;struct Param { string name; optional<int> value; // 기본값이 정수일 때만 들어 있다 int from = -1; // 기본값이 앞 매개변수의 값이면 그 자리};// 인자를 매개변수에 맞춰 넣는다. 오류가 없으면 values를 채우고 빈 문자열을, 있으면 오류 문자열을 돌려준다.string bindArgs(const vector<Param>& params, const vector<string>& args, vector<int>& values) { int p = (int)params.size(); vector<optional<int>> slot(p); // 비어 있음 = 아직 값이 들어가지 않음(0도 들어간 값이다) int positional = 0; bool seenNamed = false; for (const string& arg : args) { size_t eq = arg.find('='); if (eq != string::npos) { seenNamed = true; string name = arg.substr(0, eq); auto it = find_if(params.begin(), params.end(), [&](const Param& x) { return x.name == name; }); if (it == params.end()) return "unknown " + name; int i = (int)(it - params.begin()); if (slot[i].has_value()) return "twice " + name; slot[i] = stoi(arg.substr(eq + 1)); } else { if (seenNamed) return "order"; if (positional == p) return "many"; slot[positional++] = stoi(arg); } } values.assign(p, 0); for (int i = 0; i < p; i++) { if (slot[i].has_value()) values[i] = *slot[i]; else if (params[i].from >= 0) values[i] = values[params[i].from]; else if (params[i].value.has_value()) values[i] = *params[i].value; else return "missing " + params[i].name; } return "";}int main() { string line; getline(cin, line); int p = stoi(line); vector<Param> params; for (int i = 0; i < p; i++) { getline(cin, line); istringstream in(line); Param x; string second; in >> x.name; if (in >> second) { if (isalpha((unsigned char)second[0])) { for (int j = 0; j < i; j++) if (params[j].name == second) x.from = j; } else { x.value = stoi(second); } } params.push_back(x); } getline(cin, line); int q = stoi(line); for (int t = 0; t < q; t++) { getline(cin, line); istringstream in(line); string word; in >> word; // call vector<string> args; while (in >> word) args.push_back(word); vector<int> values; string error = bindArgs(params, args, values); if (!error.empty()) { cout << "error " << error << "\n"; } else { for (int i = 0; i < p; i++) cout << (i ? " " : "") << values[i]; cout << "\n"; } } return 0;}
Cmain.c · 88 行
#include <ctype.h>#include <stdbool.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#define MAXP 8static int p;static char names[MAXP][16];static bool has_default[MAXP]; /* 기본값이 정수인가 */static int default_value[MAXP];static int default_from[MAXP]; /* 앞 매개변수의 자리, 없으면 -1 */static int find_param(const char *name) { for (int i = 0; i < p; i++) if (strcmp(names[i], name) == 0) return i; return -1;}/* 인자를 매개변수에 맞춰 넣는다. 오류가 없으면 values를 채우고 true를, 오류가 있으면 error에 오류 문자열을 쓰고 false를 돌려준다. */static bool bind(char *args, int values[], char *error) { bool filled[MAXP] = {false}; /* 0도 들어간 값이라 값과 따로 적어 둔다 */ int positional = 0; bool seen_named = false; for (char *arg = strtok(args, " \r\n"); arg; arg = strtok(NULL, " \r\n")) { char *eq = strchr(arg, '='); if (eq) { seen_named = true; *eq = '\0'; int i = find_param(arg); if (i < 0) { sprintf(error, "unknown %s", arg); return false; } if (filled[i]) { sprintf(error, "twice %s", arg); return false; } values[i] = atoi(eq + 1); filled[i] = true; } else { if (seen_named) { strcpy(error, "order"); return false; } if (positional == p) { strcpy(error, "many"); return false; } values[positional] = atoi(arg); filled[positional] = true; positional++; } } for (int i = 0; i < p; i++) { if (filled[i]) continue; if (default_from[i] >= 0) values[i] = values[default_from[i]]; else if (has_default[i]) values[i] = default_value[i]; else { sprintf(error, "missing %s", names[i]); return false; } } return true;}int main(void) { static char line[1024]; if (!fgets(line, sizeof line, stdin)) return 0; p = atoi(line); for (int i = 0; i < p; i++) { char second[32] = ""; if (!fgets(line, sizeof line, stdin)) return 0; int got = sscanf(line, "%15s %31s", names[i], second); default_from[i] = -1; has_default[i] = false; if (got == 2) { if (isalpha((unsigned char)second[0])) { default_from[i] = find_param(second); } else { has_default[i] = true; default_value[i] = atoi(second); } } } if (!fgets(line, sizeof line, stdin)) return 0; int q = atoi(line); for (int t = 0; t < q; t++) { if (!fgets(line, sizeof line, stdin)) break; int values[MAXP]; char error[64]; /* 줄 맨 앞의 call은 건너뛴다 */ if (bind(line + 4, values, error)) { for (int i = 0; i < p; i++) printf(i ? " %d" : "%d", values[i]); printf("\n"); } else { printf("error %s\n", error); } } return 0;}