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 には名前付き引数がないので、オブジェクトを1つ渡して 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 は値の個数が違うと止まります。 罠その1: 渡した値が 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 が既定値に変わります。 罠その2: 既定値 0 も既定値です。 JS if (defaultValue[i]) で聞くと depth 0 が既定値のない仮引数になります。 罠その3: h w の既定値はその呼び出しで w が受け取った値です。 宣言を読むときに前の仮引数の既定値を写しておくと、2つ目の例の call 4 が 4 10 10 になります。 前の仮引数から順に埋めれば済みます。 罠その4: エラーは引数を左から見て最初に当たった1つです。 種類ごとに別々に調べると call pad=2 4 nope=1 で order ではなく unknown が出ます。 missing も宣言順でいちばん前のものなので、call h=7 は pad ではなく w です。 罠その5: 名前は全体が一致しなければなりません。 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;}