{ }codelper
引数P0361

名前付き引数の割り当て

関数1つの仮引数の一覧と、その関数の呼び出しがいくつか与えられる。
呼び出しごとに、各引数がどの仮引数に入るかを決めて、仮引数が受け取る値を出力したい。
1行目に仮引数の数 p が、続く p 行に仮引数が宣言順に1行に1つずつ与えられる。
仮引数の行は「名前」「名前 整数」「名前 前の名前」のどれかである。
「名前」だけなら既定値のない仮引数で、「名前 整数」は既定値がその整数の仮引数である。
「名前 前の名前」は既定値が「その呼び出しで前の名前の仮引数が受け取った値」である仮引数で、前の名前はそれより前の行で宣言された仮引数の名前である。
次の行に呼び出しの数 q が、続く q 行に呼び出しが1行に1つずつ与えられる。
呼び出しの行は call で始まり、その後ろに0個以上の引数が空白1つで区切って続く。
引数は、整数1つの位置引数か、「名前=整数」の形の名前付き引数である。
呼び出しごとに、引数を左から1つずつ処理する。
その呼び出しの k 番目の位置引数は k 番目に宣言された仮引数に入り、名前付き引数はその名前の仮引数に入る。
処理の途中で次のエラーに当たったら、そのエラー1行だけを出力し、その呼び出しの残りの引数は見ない。
位置引数が名前付き引数より後ろに出てきたら "error order" である。
そうでなく、位置引数が p 個を超えたら "error many" である。
名前付き引数の名前が仮引数にないなら "error unknown 名前" である。
名前はあるが、その仮引数にこの呼び出しですでに値が入っているなら "error twice 名前" である。
すべての引数をエラーなく処理したら、値が入っていない仮引数を宣言順に既定値で埋める。
埋めている途中で既定値のない仮引数に当たったら "error missing 名前" を出力してその呼び出しを終える。
エラーがなければ、p 個の仮引数が受け取った値を宣言順に空白1つで区切って1行に出力せよ。
呼び出しは互いに影響しない。

入力
1 ≤ p ≤ 8、1 ≤ q ≤ 1,000。
名前は英小文字1〜8文字で、仮引数の名前は互いに異なり call ではない。
整数はすべて −1,000,000,000 以上 1,000,000,000 以下で、負の数は先頭に - が付く。
1つの呼び出しの引数は12個以下。
名前付き引数の = の前後に空白はない
出力
呼び出しごとに1行: 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 には名前付き引数がないので、オブジェクトを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 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));}