3 5
5 2 4
L sub 20
R sub 20
L gap 0
R gap 0
L max -7
stdout
9
-13
1
3
5
main.py
错误列表00F8 ↓ · ⇧F8 ↑
输入(stdin)
运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。
$ 就绪
请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。
其他语言怎么写?查看各语言题解 · 8 种语言题解
把函数当作值来用,各语言的写法不同。 这里把函数放进“名字 → 函数”的表里,再作为参数传给折叠函数。 Python 的函数名本身就是值,所以内置函数也可以直接放进去:{"add": operator.add, "max": max}。 JS、TS 用箭头函数 (p, q) => p - q。 Java 把 lambda 或 Long::sum、Math::max 这样的方法引用放进 LongBinaryOperator,用 f.applyAsLong(p, q) 调用。 C# 把 lambda 或 Math.Max 放进 Func<long, long, long>。 C 用函数指针 long long (*f)(long long, long long),C++ 把 lambda 放进 std::function。 Craykon 没有 lambda,所以要定义有名字的函数,像 ops["sub"] = 减; 这样不加括号只写名字放进去,再用 f(acc, x) 调用。 陷阱一:右折叠不只是遍历方向变了,参数的位置也变了。 从右边遍历却用 f(v, a_i) 调用,样例的 R sub 20 会变成 ((20 - 4) - 2) - 5 = 9(答案是 -13)。 JS 的 reduceRight 也是按 f(到目前为止的值, 元素) 的顺序调用,直接用就会这样出错。 反过来只换参数位置却仍从左边遍历也是错的,不区分 L 和 R 也是错的。 陷阱二:在 JS 里不要像 list.reduce(Math.max, init) 这样直接传内置函数。 reduce 会给函数传(到目前为止的值, 元素, 下标, 数组)四个参数,而 Math.max 会把收到的全部转成数来比较,结果是 NaN(样例的 L max -7)。 要用只接收两个参数的箭头函数包一层。 陷阱三:不要漏掉初始值。 Python 的 functools.reduce(f, items) 不给初始值时从第一个元素开始,样例的 L sub 20 就不是 9 而是 (5 - 2) - 4 = -1。 陷阱四:把 1,000 个 10 亿相加是 1 万亿,超过 32 位。 Java、C# 用 long,C、C++ 用 long long 来折叠。 在 C++ 里把到目前为止的值放进 int,不报错却得到错误的数。 Python 的整数没有上限,JS、TS 和 Craykon 的数在 2^53 以内是精确的。
Pythonmain.py · 33 行
import sysfrom operator import add, subdef gap(p, q): return abs(p - q)# 이름 → 함수. 함수도 값이라서 사전에 넣을 수 있다 (max, min은 내장 함수 그대로)OPS = {"add": add, "sub": sub, "max": max, "min": min, "gap": gap}def fold_left(items, f, init): acc = init for x in items: acc = f(acc, x) # 지금까지의 값이 왼쪽 return accdef fold_right(items, f, init): acc = init for x in reversed(items): acc = f(x, acc) # 지금까지의 값이 오른쪽 return accdef main(): lines = sys.stdin.read().split("\n") n, q = map(int, lines[0].split()) items = [int(v) for v in lines[1].split()] out = [] for line in lines[2:2 + q]: side, name, init = line.split() fold = fold_left if side == "L" else fold_right out.append(fold(items, OPS[name], int(init))) print("\n".join(map(str, out)))main()
JavaScriptmain.js · 32 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const [n, q] = lines[0].trim().split(/\s+/).map(Number);const items = lines[1].trim().split(/\s+/).map(Number);// 이름 → 함수. 인수를 꼭 두 개만 받도록 화살표 함수로 감싼다const OPS = { add: (p, q) => p + q, sub: (p, q) => p - q, max: (p, q) => Math.max(p, q), min: (p, q) => Math.min(p, q), gap: (p, q) => Math.abs(p - q),};function foldLeft(list, f, init) { let acc = init; for (const x of list) acc = f(acc, x); // 지금까지의 값이 왼쪽 return acc;}function foldRight(list, f, init) { let acc = init; for (let i = list.length - 1; i >= 0; i--) acc = f(list[i], acc); // 지금까지의 값이 오른쪽 return acc;}const out = [];for (let k = 0; k < q; k++) { const [side, name, init] = lines[2 + k].trim().split(/\s+/); const fold = side === "L" ? foldLeft : foldRight; out.push(fold(items, OPS[name], Number(init)));}console.log(out.join("\n"));
TypeScriptmain.ts · 37 行
import { readFileSync } from "node:fs";type Op = (p: number, q: number) => number;type Fold = (list: number[], f: Op, init: number) => number;const lines: string[] = readFileSync(0, "utf8").split("\n");const [n, q] = lines[0].trim().split(/\s+/).map(Number);const items: number[] = lines[1].trim().split(/\s+/).map(Number);// 이름 → 함수. 인수를 꼭 두 개만 받도록 화살표 함수로 감싼다const OPS: Record<string, Op> = { add: (p, q) => p + q, sub: (p, q) => p - q, max: (p, q) => Math.max(p, q), min: (p, q) => Math.min(p, q), gap: (p, q) => Math.abs(p - q),};const foldLeft: Fold = (list, f, init) => { let acc = init; for (const x of list) acc = f(acc, x); // 지금까지의 값이 왼쪽 return acc;};const foldRight: Fold = (list, f, init) => { let acc = init; for (let i = list.length - 1; i >= 0; i--) acc = f(list[i], acc); // 지금까지의 값이 오른쪽 return acc;};const out: number[] = [];for (let k = 0; k < q; k++) { const [side, name, init] = lines[2 + k].trim().split(/\s+/); const fold: Fold = side === "L" ? foldLeft : foldRight; out.push(fold(items, OPS[name], Number(init)));}console.log(out.join("\n"));
JavaMain.java · 48 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.HashMap;import java.util.Map;import java.util.StringTokenizer;import java.util.function.LongBinaryOperator;public class Main { static long foldLeft(long[] items, LongBinaryOperator f, long init) { long acc = init; for (long x : items) acc = f.applyAsLong(acc, x); // 지금까지의 값이 왼쪽 return acc; } static long foldRight(long[] items, LongBinaryOperator f, long init) { long acc = init; for (int i = items.length - 1; i >= 0; i--) acc = f.applyAsLong(items[i], acc); // 지금까지의 값이 오른쪽 return acc; } public static void main(String[] args) throws Exception { // 이름 → 함수. 이미 있는 메서드는 Long::sum처럼, 없는 것은 람다로 적는다 Map<String, LongBinaryOperator> ops = new HashMap<>(); ops.put("add", Long::sum); ops.put("sub", (p, q) -> p - q); ops.put("max", Math::max); ops.put("min", Math::min); ops.put("gap", (p, q) -> Math.abs(p - q)); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); int n = Integer.parseInt(st.nextToken()); int q = Integer.parseInt(st.nextToken()); long[] items = new long[n]; st = new StringTokenizer(br.readLine()); for (int i = 0; i < n; i++) items[i] = Long.parseLong(st.nextToken()); StringBuilder out = new StringBuilder(); for (int k = 0; k < q; k++) { st = new StringTokenizer(br.readLine()); String side = st.nextToken(); LongBinaryOperator f = ops.get(st.nextToken()); long init = Long.parseLong(st.nextToken()); out.append(side.equals("L") ? foldLeft(items, f, init) : foldRight(items, f, init)).append('\n'); } System.out.print(out); }}
C#Main.cs · 47 行
using System;using System.Collections.Generic;using System.Text;class Program{ static long FoldLeft(long[] items, Func<long, long, long> f, long init) { long acc = init; foreach (long x in items) acc = f(acc, x); // 지금까지의 값이 왼쪽 return acc; } static long FoldRight(long[] items, Func<long, long, long> f, long init) { long acc = init; for (int i = items.Length - 1; i >= 0; i--) acc = f(items[i], acc); // 지금까지의 값이 오른쪽 return acc; } static void Main() { // 이름 → 함수. Math.Max는 형이 여러 가지라 Func<long, long, long>에 넣으면 long용이 골라진다 var ops = new Dictionary<string, Func<long, long, long>> { ["add"] = (p, q) => p + q, ["sub"] = (p, q) => p - q, ["max"] = Math.Max, ["min"] = Math.Min, ["gap"] = (p, q) => Math.Abs(p - q), }; string[] first = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries); int q2 = int.Parse(first[1]); long[] items = Array.ConvertAll(Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries), long.Parse); var sb = new StringBuilder(); for (int k = 0; k < q2; k++) { string[] parts = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries); Func<long, long, long> f = ops[parts[1]]; long init = long.Parse(parts[2]); sb.Append(parts[0] == "L" ? FoldLeft(items, f, init) : FoldRight(items, f, init)).Append('\n'); } Console.Write(sb); }}
C++main.cpp · 47 行
#include <algorithm>#include <cstdlib>#include <functional>#include <iostream>#include <map>#include <string>#include <vector>using Op = std::function<long long(long long, long long)>;long long foldLeft(const std::vector<long long> &items, const Op &f, long long init) { long long acc = init; for (long long x : items) acc = f(acc, x); // 지금까지의 값이 왼쪽 return acc;}long long foldRight(const std::vector<long long> &items, const Op &f, long long init) { long long acc = init; for (auto it = items.rbegin(); it != items.rend(); ++it) acc = f(*it, acc); // 지금까지의 값이 오른쪽 return acc;}int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); // 이름 → 함수. 람다를 std::function에 담는다 const std::map<std::string, Op> ops = { {"add", [](long long p, long long q) { return p + q; }}, {"sub", [](long long p, long long q) { return p - q; }}, {"max", [](long long p, long long q) { return std::max(p, q); }}, {"min", [](long long p, long long q) { return std::min(p, q); }}, {"gap", [](long long p, long long q) { return std::llabs(p - q); }}, }; int n, q; std::cin >> n >> q; std::vector<long long> items(n); for (auto &x : items) std::cin >> x; for (int k = 0; k < q; k++) { std::string side, name; long long init; std::cin >> side >> name >> init; const Op &f = ops.at(name); std::cout << (side == "L" ? foldLeft(items, f, init) : foldRight(items, f, init)) << '\n'; }}
Cmain.c · 46 行
#include <stdio.h>#include <string.h>/* 함수를 가리키는 포인터의 형: long long 둘을 받아 long long을 돌려주는 함수 */typedef long long (*Op)(long long, long long);long long op_add(long long p, long long q) { return p + q; }long long op_sub(long long p, long long q) { return p - q; }long long op_max(long long p, long long q) { return p > q ? p : q; }long long op_min(long long p, long long q) { return p < q ? p : q; }long long op_gap(long long p, long long q) { return p > q ? p - q : q - p; }/* 이름 → 함수 표 */struct { const char *name; Op f; } OPS[] = { {"add", op_add}, {"sub", op_sub}, {"max", op_max}, {"min", op_min}, {"gap", op_gap},};long long fold_left(const long long *items, int n, Op f, long long init) { long long acc = init; for (int i = 0; i < n; i++) acc = f(acc, items[i]); /* 지금까지의 값이 왼쪽 */ return acc;}long long fold_right(const long long *items, int n, Op f, long long init) { long long acc = init; for (int i = n - 1; i >= 0; i--) acc = f(items[i], acc); /* 지금까지의 값이 오른쪽 */ return acc;}long long items[1000];int main(void) { int n, q; scanf("%d %d", &n, &q); for (int i = 0; i < n; i++) scanf("%lld", &items[i]); for (int k = 0; k < q; k++) { char side[4], name[8]; long long init; scanf("%3s %7s %lld", side, name, &init); Op f = NULL; for (int j = 0; j < 5; j++) if (strcmp(OPS[j].name, name) == 0) f = OPS[j].f; printf("%lld\n", side[0] == 'L' ? fold_left(items, n, f, init) : fold_right(items, n, f, init)); } return 0;}
Craykonmain.cray · 70 行
더하기(p, q){ 返回 p + q;}덜기(p, q){ 返回 p - q;}큰쪽(p, q){ 返回 数学.较大值(p, q);}작은쪽(p, q){ 返回 数学.较小值(p, q);}차이(p, q){ 返回 数学.绝对值(p - q);}// 이름 → 함수. 함수 이름을 괄호 없이 쓰면 함수 자체가 값이 된다ops = 字典();ops["add"] = 더하기;ops["sub"] = 덜기;ops["max"] = 큰쪽;ops["min"] = 작은쪽;ops["gap"] = 차이;왼쪽접기(items, f, init){ acc = init; 遍历 (x 在 items) { acc = f(acc, x); // 지금까지의 값이 왼쪽 } 返回 acc;}오른쪽접기(items, f, init){ acc = init; 循环 (i = items.数量 - 1; i >= 0; i--) { acc = f(items[i], acc); // 지금까지의 값이 오른쪽 } 返回 acc;}첫줄 = 控制台.读数字();q = 첫줄[1];items = 控制台.读数字();循环 (k = 0; k < q; k++){ parts = 控制台.读行().分割(" "); f = ops[parts[1]]; init = parts[2].转数字(); 如果 (parts[0] == "L") { 控制台.输出(왼쪽접기(items, f, init)); } 否则 { 控制台.输出(오른쪽접기(items, f, init)); }}