{ }codelper
交换值分割字符串P0272

一次性赋值的计算器格子

一个小计算器有 26 个格子,分别以英文小写字母 a 到 z 命名。
所有格子一开始都是 0。
第一行给出命令数 q,接下来 q 行每行一条命令。
命令中的单词用一个空格分隔。
set x v 把整数 v 放进格子 x。
swap x y 交换格子 x 和格子 y 的值。
x 和 y 可能相同,这时什么也不变。
rot x y z 轮换三个不同格子的值:x 得到 y 原来的值,y 得到 z 原来的值,z 得到 x 原来的值。
let x1 x2 … xk = e1 e2 … ek 把项 ei 的值放进格子 xi(i = 1, …, k)。
左边的格子名互不相同。
这时先用执行这条命令之前的值把右边 k 个项全部算出来,然后再一次性放进 k 个格子。
项是一个格子名、一个非负整数,或者 u+w、u-w 的形式。
u 和 w 各是一个格子名或非负整数,中间没有空格。
show x1 x2 … xk 按这个顺序把格子 x1, x2, …, xk 的值用一个空格隔开输出在一行。
同一个格子可能出现多次。
依次处理命令,每个 show 输出一行。

输入
1 ≤ q ≤ 2,000。
set 的 v 在 -1,000,000 到 1,000,000 之间。
项中的整数在 0 到 1,000,000 之间。
let 和 show 的格子数 k 在 1 到 26 之间。
show 有 1 到 200 个。
所有格子的值始终在 -1,000,000,000 到 1,000,000,000 之间
输出
与 show 个数相同的行数
样例 1 · stdin
7
set a 1
let a b = a+b a
let a b = a+b a
let a b = a+b a
show a b
swap a b
show a b
stdout
3 2
2 3
样例 2 · stdin
9
set x 5
set y -7
set z 9
swap x x
show x
rot x y z
show x y z
let x y z = z-1 x+y 100
show x y z
stdout
5
-7 9 5
4 2 100
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

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

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

交换两个值的方法因语言而异。
有一次性赋值的写法时,比如 Python a, b = b, a、JS・TS [a, b] = [b, a]、C# (a, b) = (b, a),会先算完右边再赋值,所以 rot 也只要一行。
C++ 没有这种写法,但有 std::swap(a, b) 函数;Java、C、Craykon 则先把一个值寄存在临时变量 tmp 里。
let 的格子数 k 每次都不同,所以无论哪种语言,都要先把右边的值收集到数组(Python、Craykon 用列表)里再赋值。
陷阱 ① 每算出一个 let 的值就马上赋值:在 let a b = a+b a 中先改了 a,b 就会得到新的 a。
② 起个新名字不等于复制:Python old = reg、JS const old = reg 指向同一个字典・数组,并不是快照。
要用 dict(reg)、[...reg] 复制。
③ 不用临时变量的交换技巧:XOR(a ^= b; b ^= a; a ^= b)和加减法(a = a + b; b = a - b; a = a - b)在两个名字指向同一个格子时(swap x x)会把值变成 0。
加减法遇到大数还可能溢出。
④ 不用临时变量把 rot 写成 x = y; y = z; z = x,z 会得到已经改过的 x(原来的 y)。
方向也容易搞反:x 是接收 y 的值的那一个。
⑤ 项里的 - 总是减法。
项中的整数没有符号,所以在第一个字符之后找 + 或 - 就行。
Craykon 中,函数里赋值的名字如果在程序最外层也用到(且不是参数),就会改动外面的那个变量,所以函数里的循环变量要取与外面不同的名字。

Pythonmain.py · 42 行
import sys lines = sys.stdin.read().split("\n")q = int(lines[0])reg = {chr(c): 0 for c in range(ord("a"), ord("z") + 1)}  def atom(t):    return reg[t] if t.isalpha() else int(t)  def term(t):    # p+q, p-q, 또는 칸 이름·수 하나. 항 안의 수에는 부호가 없어서 -는 늘 빼기다    for op in "+-":        i = t.find(op)        if i > 0:            a, b = atom(t[:i]), atom(t[i + 1:])            return a + b if op == "+" else a - b    return atom(t)  out = []for line in lines[1:1 + q]:    p = line.split()    cmd = p[0]    if cmd == "set":        reg[p[1]] = int(p[2])    elif cmd == "swap":        x, y = p[1], p[2]        reg[x], reg[y] = reg[y], reg[x]  # x와 y가 같아도 그대로    elif cmd == "rot":        x, y, z = p[1], p[2], p[3]        reg[x], reg[y], reg[z] = reg[y], reg[z], reg[x]    elif cmd == "let":        eq = p.index("=")        targets = p[1:eq]        values = [term(t) for t in p[eq + 1:]]  # 오른쪽을 모두 먼저 계산한다        for x, v in zip(targets, values):            reg[x] = v    else:  # show        out.append(" ".join(str(reg[x]) for x in p[1:]))print("\n".join(out))
JavaScriptmain.js · 40 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const reg = new Array(26).fill(0);const at = (x) => x.charCodeAt(0) - 97; // 'a' → 0 const atom = (t) => (/^[a-z]$/.test(t) ? reg[at(t)] : Number(t)); function term(t) {  // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다  const i = t.search(/[+-]/);  if (i <= 0) return atom(t);  const a = atom(t.slice(0, i));  const b = atom(t.slice(i + 1));  return t[i] === "+" ? a + b : a - b;} const out = [];for (const line of lines.slice(1, 1 + q)) {  const p = line.trim().split(" ");  const cmd = p[0];  if (cmd === "set") {    reg[at(p[1])] = Number(p[2]);  } else if (cmd === "swap") {    const x = at(p[1]);    const y = at(p[2]);    [reg[x], reg[y]] = [reg[y], reg[x]]; // 구조 분해로 한꺼번에 바꾼다  } else if (cmd === "rot") {    const [x, y, z] = p.slice(1).map(at);    [reg[x], reg[y], reg[z]] = [reg[y], reg[z], reg[x]];  } else if (cmd === "let") {    const eq = p.indexOf("=");    const values = p.slice(eq + 1).map(term); // 오른쪽을 모두 먼저 계산한다    p.slice(1, eq).forEach((x, i) => {      reg[at(x)] = values[i];    });  } else {    out.push(p.slice(1).map((x) => reg[at(x)]).join(" "));  }}console.log(out.join("\n"));
TypeScriptmain.ts · 42 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const reg: number[] = new Array<number>(26).fill(0);const at = (x: string): number => x.charCodeAt(0) - 97; // 'a' → 0 const atom = (t: string): number => (/^[a-z]$/.test(t) ? reg[at(t)] : Number(t)); function term(t: string): number {  // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다  const i = t.search(/[+-]/);  if (i <= 0) return atom(t);  const a = atom(t.slice(0, i));  const b = atom(t.slice(i + 1));  return t[i] === "+" ? a + b : a - b;} const out: string[] = [];for (const line of lines.slice(1, 1 + q)) {  const p = line.trim().split(" ");  const cmd = p[0];  if (cmd === "set") {    reg[at(p[1])] = Number(p[2]);  } else if (cmd === "swap") {    const x = at(p[1]);    const y = at(p[2]);    [reg[x], reg[y]] = [reg[y], reg[x]]; // 구조 분해로 한꺼번에 바꾼다  } else if (cmd === "rot") {    const [x, y, z] = p.slice(1).map(at);    [reg[x], reg[y], reg[z]] = [reg[y], reg[z], reg[x]];  } else if (cmd === "let") {    const eq = p.indexOf("=");    const values: number[] = p.slice(eq + 1).map(term); // 오른쪽을 모두 먼저 계산한다    p.slice(1, eq).forEach((x, i) => {      reg[at(x)] = values[i];    });  } else {    out.push(p.slice(1).map((x) => reg[at(x)]).join(" "));  }}console.log(out.join("\n"));
JavaMain.java · 68 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.StringJoiner; public class Main {    static int[] reg = new int[26];     static int at(String x) {        return x.charAt(0) - 'a';    }     static int atom(String t) {        return Character.isLetter(t.charAt(0)) ? reg[at(t)] : Integer.parseInt(t);    }     static int term(String t) {        // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다        for (int i = 1; i < t.length(); i++) {            char c = t.charAt(i);            if (c == '+' || c == '-') {                int a = atom(t.substring(0, i));                int b = atom(t.substring(i + 1));                return c == '+' ? a + b : a - b;            }        }        return atom(t);    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int q = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int t = 0; t < q; t++) {            String[] p = in.readLine().trim().split(" ");            switch (p[0]) {                case "set" -> reg[at(p[1])] = Integer.parseInt(p[2]);                case "swap" -> {                    int x = at(p[1]), y = at(p[2]);                    int tmp = reg[x]; // Java에는 a, b = b, a가 없어서 임시 변수를 쓴다                    reg[x] = reg[y];                    reg[y] = tmp;                }                case "rot" -> {                    int x = at(p[1]), y = at(p[2]), z = at(p[3]);                    int tmp = reg[x];                    reg[x] = reg[y];                    reg[y] = reg[z];                    reg[z] = tmp;                }                case "let" -> {                    int eq = 1;                    while (!p[eq].equals("=")) eq++;                    int k = eq - 1;                    int[] values = new int[k]; // 오른쪽을 모두 먼저 계산해 둔다                    for (int i = 0; i < k; i++) values[i] = term(p[eq + 1 + i]);                    for (int i = 0; i < k; i++) reg[at(p[1 + i])] = values[i];                }                default -> {                    StringJoiner sj = new StringJoiner(" ");                    for (int i = 1; i < p.length; i++) sj.add(String.valueOf(reg[at(p[i])]));                    out.append(sj).append('\n');                }            }        }        System.out.print(out);    }}
C#Main.cs · 62 行
using System;using System.Linq;using System.Text; class Program{    static int[] reg = new int[26];     static int At(string x) => x[0] - 'a';     static int Atom(string t) => char.IsLetter(t[0]) ? reg[At(t)] : int.Parse(t);     static int Term(string t)    {        // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다        int i = t.IndexOfAny(new[] { '+', '-' }, 1);        if (i < 0) return Atom(t);        int a = Atom(t.Substring(0, i));        int b = Atom(t.Substring(i + 1));        return t[i] == '+' ? a + b : a - b;    }     static void Main()    {        int q = int.Parse(Console.ReadLine().Trim());        var sb = new StringBuilder();        for (int t = 0; t < q; t++)        {            string[] p = Console.ReadLine().Trim().Split(' ');            switch (p[0])            {                case "set":                    reg[At(p[1])] = int.Parse(p[2]);                    break;                case "swap":                {                    int x = At(p[1]), y = At(p[2]);                    (reg[x], reg[y]) = (reg[y], reg[x]); // 튜플로 한꺼번에 바꾼다                    break;                }                case "rot":                {                    int x = At(p[1]), y = At(p[2]), z = At(p[3]);                    (reg[x], reg[y], reg[z]) = (reg[y], reg[z], reg[x]);                    break;                }                case "let":                {                    int eq = Array.IndexOf(p, "=");                    // 오른쪽을 모두 먼저 계산해 배열로 받아 둔다                    int[] values = p.Skip(eq + 1).Select(Term).ToArray();                    for (int i = 0; i < values.Length; i++) reg[At(p[1 + i])] = values[i];                    break;                }                default:                    sb.Append(string.Join(" ", p.Skip(1).Select(x => reg[At(x)]))).Append('\n');                    break;            }        }        Console.Write(sb);    }}
C++main.cpp · 58 行
#include <array>#include <iostream>#include <sstream>#include <string>#include <utility>#include <vector> std::array<int, 26> reg{}; int atom(const std::string &t) {    return std::isalpha(static_cast<unsigned char>(t[0])) ? reg[t[0] - 'a'] : std::stoi(t);} int term(const std::string &t) {    // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다    std::size_t i = t.find_first_of("+-", 1);    if (i == std::string::npos) return atom(t);    int a = atom(t.substr(0, i)), b = atom(t.substr(i + 1));    return t[i] == '+' ? a + b : a - b;} int main() {    std::string line;    std::getline(std::cin, line);    int q = std::stoi(line);    std::string out;    for (int c = 0; c < q; c++) {        std::getline(std::cin, line);        std::istringstream ss(line);        std::vector<std::string> p;        for (std::string w; ss >> w;) p.push_back(w);        if (p[0] == "set") {            reg[p[1][0] - 'a'] = std::stoi(p[2]);        } else if (p[0] == "swap") {            std::swap(reg[p[1][0] - 'a'], reg[p[2][0] - 'a']);        } else if (p[0] == "rot") {            int &x = reg[p[1][0] - 'a'], &y = reg[p[2][0] - 'a'], &z = reg[p[3][0] - 'a'];            int tmp = x;            x = y;            y = z;            z = tmp;        } else if (p[0] == "let") {            std::size_t eq = 1;            while (p[eq] != "=") eq++;            std::vector<int> values; // 오른쪽을 모두 먼저 계산한다            for (std::size_t i = eq + 1; i < p.size(); i++) values.push_back(term(p[i]));            for (std::size_t i = 0; i < values.size(); i++) reg[p[1 + i][0] - 'a'] = values[i];        } else {            for (std::size_t i = 1; i < p.size(); i++) {                if (i > 1) out += ' ';                out += std::to_string(reg[p[i][0] - 'a']);            }            out += '\n';        }    }    std::cout << out;    return 0;}
Cmain.c · 60 行
#include <ctype.h>#include <stdio.h>#include <stdlib.h>#include <string.h> static int reg[26]; static int atom(const char *t, int len) {    if (isalpha((unsigned char)t[0])) return reg[t[0] - 'a'];    int v = 0;    for (int i = 0; i < len; i++) v = v * 10 + (t[i] - '0');    return v;} /* p+q, p-q, 또는 칸 이름·수 하나. 항 안의 수에는 부호가 없다 */static int term(const char *t) {    int n = (int)strlen(t);    for (int i = 1; i < n; i++)        if (t[i] == '+' || t[i] == '-') {            int a = atom(t, i), b = atom(t + i + 1, n - i - 1);            return t[i] == '+' ? a + b : a - b;        }    return atom(t, n);} int main(void) {    static char line[1024];    char *tok[64];    fgets(line, sizeof line, stdin);    int q = atoi(line);    for (int c = 0; c < q; c++) {        fgets(line, sizeof line, stdin);        int k = 0;        for (char *s = strtok(line, " \r\n"); s; s = strtok(NULL, " \r\n")) tok[k++] = s;        if (strcmp(tok[0], "set") == 0) {            reg[tok[1][0] - 'a'] = atoi(tok[2]);        } else if (strcmp(tok[0], "swap") == 0) {            int x = tok[1][0] - 'a', y = tok[2][0] - 'a';            int tmp = reg[x]; /* 임시 칸에 하나를 맡겨 둔다 */            reg[x] = reg[y];            reg[y] = tmp;        } else if (strcmp(tok[0], "rot") == 0) {            int x = tok[1][0] - 'a', y = tok[2][0] - 'a', z = tok[3][0] - 'a';            int tmp = reg[x];            reg[x] = reg[y];            reg[y] = reg[z];            reg[z] = tmp;        } else if (strcmp(tok[0], "let") == 0) {            int eq = 1;            while (strcmp(tok[eq], "=") != 0) eq++;            int n = eq - 1, values[26];            for (int i = 0; i < n; i++) values[i] = term(tok[eq + 1 + i]); /* 먼저 모두 계산 */            for (int i = 0; i < n; i++) reg[tok[1 + i][0] - 'a'] = values[i];        } else {            for (int i = 1; i < k; i++) printf(i > 1 ? " %d" : "%d", reg[tok[i][0] - 'a']);            putchar('\n');        }    }    return 0;}
Craykonmain.cray · 77 行
q = 控制台.读行().转数字();reg = 字典();循环 (i = 0; i < 26; i++){    reg[字符.从编码(97 + i)] = 0;} 하나(t){    如果 (reg.包含(t))    {        返回 reg[t];    }    返回 t.转数字();} // p+q, p-q, 또는 칸 이름·수 하나. 항 안의 수에는 부호가 없다항(t){    // 함수 안에서 넣은 이름이 밖에도 있으면 밖의 변수가 바뀌니, 밖에서 안 쓰는 이름(w)을 쓴다    循环 (w = 1; w < t.长度; w++)    {        如果 (t[w] == "+" || t[w] == "-")        {            가 = 하나(t.截取(0, w));            나머지 = 하나(t.截取(w + 1));            如果 (t[w] == "+")            {                返回 가 + 나머지;            }            返回 가 - 나머지;        }    }    返回 하나(t);} 循环 (c = 0; c < q; c++){    p = 控制台.读行().去空白().分割(" ");    选择 (p[0])    {        情况 "set":            reg[p[1]] = p[2].转数字();            跳出;        情况 "swap":            임시 = reg[p[1]]; // 크레이콘에는 한꺼번에 넣기가 없어서 임시 변수에 맡긴다            reg[p[1]] = reg[p[2]];            reg[p[2]] = 임시;            跳出;        情况 "rot":            임시 = reg[p[1]];            reg[p[1]] = reg[p[2]];            reg[p[2]] = reg[p[3]];            reg[p[3]] = 임시;            跳出;        情况 "let":            eq = p.查找位置("=");            값들 = []; // 오른쪽을 모두 먼저 계산해 둔다            循环 (i = eq + 1; i < p.数量; i++)            {                값들.添加(항(p[i]));            }            循环 (i = 0; i < 값들.数量; i++)            {                reg[p[1 + i]] = 값들[i];            }            跳出;        默认:            보일것 = [];            循环 (i = 1; i < p.数量; i++)            {                보일것.添加(reg[p[i]]);            }            控制台.输出(보일것.连接(" "));            跳出;    }}