{ }codelper
参数P0361

绑定命名参数

给出一个函数的形参列表,以及对这个函数的若干次调用。
对每次调用,要确定每个实参进入哪个形参,并输出各形参得到的值。
第一行是形参个数 p,接下来 p 行按声明顺序每行给出一个形参。
形参行是“名字”“名字 整数”“名字 前面的名字”三种之一。
只有“名字”表示没有默认值的形参,“名字 整数”表示默认值是该整数的形参。
“名字 前面的名字”表示默认值是“这次调用中前面那个形参得到的值”的形参,前面的名字是在更靠前的行声明的形参的名字。
下一行是调用次数 q,接下来 q 行每行给出一次调用。
调用行以 call 开头,后面跟着 0 个或多个实参,用一个空格隔开。
实参要么是一个整数的位置实参,要么是“名字=整数”形式的命名实参。
对每次调用,从左到右逐个处理实参。
这次调用的第 k 个位置实参进入第 k 个声明的形参,命名实参进入那个名字的形参。
处理过程中遇到下面的错误时,只输出那一行错误,不再看这次调用剩下的实参。
位置实参出现在命名实参之后,是 "error order"。
否则,位置实参超过 p 个,是 "error many"。
命名实参的名字不在形参里,是 "error unknown 名字"。
名字存在,但这次调用中那个形参已经有值,是 "error twice 名字"。
所有实参都没有错误地处理完后,把还没有值的形参按声明顺序用默认值填上。
填的过程中遇到没有默认值的形参,就输出 "error missing 名字" 并结束这次调用。
没有错误时,把 p 个形参得到的值按声明顺序用一个空格隔开输出在一行。
各次调用互不影响。

输入
1 ≤ p ≤ 8,1 ≤ q ≤ 1,000。
名字是 1 ~ 8 个英文小写字母,形参名字互不相同且不是 call。
整数都在 −1,000,000,000 到 1,000,000,000 之间,负数前面带 -。
一次调用的实参不超过 12 个。
命名实参的 = 两边没有空格
输出
每次调用一行:p 个值,或 error order、error many、error unknown 名字、error twice 名字、error missing 名字
样例 1 · stdin
4
w
h w
depth 0
pad
10
call 3 pad=1
call 0 0 0 0
call 5 w=0
call 0 w=5
call pad=2 4 nope=1
call h=7
call 1 2 3 4 5
call 2 dept=1 pad=0
call 6 depth=0 depth=4 pad=1
call 9 h=0 pad=0
stdout
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
输入(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 sys  def 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 8 static 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;}
Craykonmain.cray · 92 行
// 인자를 매개변수에 맞춰 넣는다. 출력할 한 줄(값들 또는 오류)을 돌려준다.맞추기(names, defaultValue, defaultFrom, args){    p = names.数量;    values = 新列表(p, 空); // 빈값 = 아직 값이 들어가지 않음(0도 들어간 값이다)    positional = 0;    seenNamed = 假;    循环 (a = 1; a < args.数量; a++)    {        arg = args[a];        eq = arg.查找位置("=");        如果 (eq >= 0)        {            seenNamed = 真;            name = arg.截取(0, eq);            i = names.查找位置(name);            如果 (i < 0)            {                返回 "error " + "unknown " + name;            }            如果 (values[i] != 空)            {                返回 "error " + "twice " + name;            }            values[i] = arg.截取(eq + 1).转数字();        }        否则        {            如果 (seenNamed)            {                返回 "error " + "order";            }            如果 (positional == p)            {                返回 "error " + "many";            }            values[positional] = arg.转数字();            positional = positional + 1;        }    }    循环 (i = 0; i < p; i++)    {        如果 (values[i] == 空)        {            如果 (defaultFrom[i] >= 0)            {                values[i] = values[defaultFrom[i]];            }            否则 如果 (defaultValue[i] != 空)            {                values[i] = defaultValue[i];            }            否则            {                返回 "error " + "missing " + names[i];            }        }    }    返回 values.连接(" ");} p = 控制台.读行().转数字();names = [];defaultValue = [];defaultFrom = [];循环 (i = 0; i < p; i++){    parts = 控制台.读行().去空白().分割(" ");    value = 空;    source = -1;    如果 (parts.数量 == 2)    {        첫글자 = parts[1].截取(0, 1);        如果 (첫글자 >= "a" && 첫글자 <= "z")        {            source = names.查找位置(parts[1]);        }        否则        {            value = parts[1].转数字();        }    }    names.添加(parts[0]);    defaultValue.添加(value);    defaultFrom.添加(source);}q = 控制台.读行().转数字();循环 (t = 0; t < q; t++){    args = 控制台.读行().去空白().分割(" ");    控制台.输出(맞추기(names, defaultValue, defaultFrom, args));}