{ }codelper
高阶函数遍历列表负数取余P0239

传入规则函数来整理数列

用 s 条规则依次整理一个整数列表。
第一行给出 n,第二行给出 n 个整数,用空格分开。
第三行给出 s,接下来 s 行每行以 "名字 k" 的形式给出一条规则。
规则有六种,前三种改变列表中的每个数,后三种只保留满足条件的数。
add k:把每个数 x 变成 x + k。
mul k:把每个数 x 变成 x × k。
mod k:把每个数 x 变成 x 除以 k 的余数 r。
这里 r 大于等于 0 且小于 k,x - r 是 k 的倍数。
所以 -7 除以 3 的余数是 2。
gt k:只保留比 k 大的数。
lt k:只保留比 k 小的数。
div k:只保留 k 的倍数。
0 和负数也可能是 k 的倍数。
留下的数保持原来的顺序。
每用一条规则,就在一行里输出用到这条规则为止的列表,用单个空格分开。
列表为空时输出 -。
对空列表再用下一条规则,仍是空列表。
解答中要写两个函数,它们接收 "列表、规则函数、k" 并返回新列表。
一个改变每个数,另一个只保留满足条件的数。
对每条规则,把这条规则的函数作为值传入,调用这两个函数之一。

输入
1 ≤ n ≤ 300,1 ≤ s ≤ 20,开始的数在 -1,000,000 到 1,000,000 之间。
add 的 -1,000,000 ≤ k ≤ 1,000,000,mul 的 -10 ≤ k ≤ 10,mod 的 1 ≤ k ≤ 1,000,gt、lt 的 -1,000,000,000 ≤ k ≤ 1,000,000,000,div 的 1 ≤ k ≤ 100。
任何一步中,列表里每个数的绝对值都不超过 1,000,000,000
输出
每条规则一行:当时的列表,为空则输出 -
样例 1 · stdin
5
3 -7 10 0 5
4
mul 2
mod 4
gt 0
add -1
stdout
6 -14 20 0 10
2 2 0 0 2
2 2 2
1 1 1
样例 2 · stdin
4
2 4 6 1
2
div 2
lt 0
stdout
2 4 6
-
main.py
输入(stdin)

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

$ 就绪

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

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

把函数当作值传递,各语言的写法不同。
Python 直接传 def 定义的函数名或 lambda x, k: x + k,JS/TS 传箭头函数 (x, k) => x + k(TS 要加上 (x: number, k: number) => number 这样的函数类型)。
Java 需要能接收 lambda 的函数式接口,所以用 IntBinaryOperator、BiPredicate<Integer, Integer>;C# 用 Func<int, int, int>、Func<int, int, bool>。
C 用函数指针 int (*rule)(int, int) 传递,C++ 把 lambda 放进 std::function<int(int, int)>。
Craykon 没有 lambda,所以要像 加(x, k) 这样给函数起名字,按名字传递,并在 字典() 里放“名字 → 函数”来选取。
第一个陷阱是负数的余数。
JS/TS、Java、C#、C、C++ 和 Craykon 的 % 跟随被除数的符号,所以 -7 % 3 是 -1。
只有 Python 的 % 给出 2。
可以改成 (x % k + k) % k,Java 也可以用 Math.floorMod(x, k)。
第二个陷阱是过滤的方法。
Python 在 for x in a: 里调用 a.remove(x),后面的元素会往前挪,紧接着的那个数会被跳过,所以要把要保留的数放进新列表。
JS/TS 的 map/filter 和这道题的两个函数都返回新列表,如果不把返回的列表存回去,什么都不会变。
gt、lt 不保留等于 k 的数,所以是 >、<,不是 >=、<=。

Pythonmain.py · 41 行
# 모든 수를 바꾸는 규칙(x, k → 새 수)def add(x, k):    return x + k def mul(x, k):    return x * k def mod(x, k):    return x % k  # Python의 %는 k가 양수면 늘 0 이상 k 미만 # 남길지 정하는 규칙(x, k → 참·거짓)def gt(x, k):    return x > k def lt(x, k):    return x < k def div(x, k):    return x % k == 0 # 함수를 값으로 받는 함수 두 개def change_all(a, rule, k):    return [rule(x, k) for x in a] def keep_only(a, rule, k):    return [x for x in a if rule(x, k)] CHANGE = {"add": add, "mul": mul, "mod": mod}KEEP = {"gt": gt, "lt": lt, "div": div} n = int(input())a = list(map(int, input().split()))s = int(input())for _ in range(s):    name, k = input().split()    k = int(k)    if name in CHANGE:        a = change_all(a, CHANGE[name], k)    else:        a = keep_only(a, KEEP[name], k)    print(" ".join(map(str, a)) if a else "-")
JavaScriptmain.js · 28 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");// 모든 수를 바꾸는 규칙(x, k → 새 수). JS의 %는 음수에서 음수가 나오니 k를 더해 한 번 더 나눈다const CHANGE = {  add: (x, k) => x + k,  mul: (x, k) => x * k,  mod: (x, k) => ((x % k) + k) % k,};// 남길지 정하는 규칙(x, k → 참·거짓)const KEEP = {  gt: (x, k) => x > k,  lt: (x, k) => x < k,  div: (x, k) => x % k === 0,};// 함수를 값으로 받는 함수 두 개. map·filter는 새 배열을 돌려준다const changeAll = (a, rule, k) => a.map((x) => rule(x, k));const keepOnly = (a, rule, k) => a.filter((x) => rule(x, k)); let a = lines[1].trim().split(/\s+/).map(Number);const s = Number(lines[2]);const out = [];for (let i = 0; i < s; i++) {  const [name, ks] = lines[3 + i].trim().split(/\s+/);  const k = Number(ks);  a = name in CHANGE ? changeAll(a, CHANGE[name], k) : keepOnly(a, KEEP[name], k);  // -0 % 3은 -0이지만 join은 "0"으로 쓴다  out.push(a.length ? a.join(" ") : "-");}console.log(out.join("\n"));
TypeScriptmain.ts · 30 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");type Change = (x: number, k: number) => number;type Keep = (x: number, k: number) => boolean;// 모든 수를 바꾸는 규칙. JS·TS의 %는 음수에서 음수가 나오니 k를 더해 한 번 더 나눈다const CHANGE: Record<string, Change> = {  add: (x, k) => x + k,  mul: (x, k) => x * k,  mod: (x, k) => ((x % k) + k) % k,};// 남길지 정하는 규칙const KEEP: Record<string, Keep> = {  gt: (x, k) => x > k,  lt: (x, k) => x < k,  div: (x, k) => x % k === 0,};// 함수를 값으로 받는 함수 두 개const changeAll = (a: number[], rule: Change, k: number): number[] => a.map((x) => rule(x, k));const keepOnly = (a: number[], rule: Keep, k: number): number[] => a.filter((x) => rule(x, k)); let a: number[] = lines[1].trim().split(/\s+/).map(Number);const s: number = Number(lines[2]);const out: string[] = [];for (let i = 0; i < s; i++) {  const [name, ks] = lines[3 + i].trim().split(/\s+/);  const k: number = Number(ks);  a = name in CHANGE ? changeAll(a, CHANGE[name], k) : keepOnly(a, KEEP[name], k);  out.push(a.length ? a.join(" ") : "-");}console.log(out.join("\n"));
JavaMain.java · 56 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.StringTokenizer;import java.util.function.BiPredicate;import java.util.function.IntBinaryOperator; public class Main {    // 함수를 값으로 받는 함수 두 개    static List<Integer> changeAll(List<Integer> a, IntBinaryOperator rule, int k) {        List<Integer> b = new ArrayList<>();        for (int x : a) b.add(rule.applyAsInt(x, k));        return b;    }     static List<Integer> keepOnly(List<Integer> a, BiPredicate<Integer, Integer> rule, int k) {        List<Integer> b = new ArrayList<>();        for (int x : a) if (rule.test(x, k)) b.add(x);        return b;    }     public static void main(String[] args) throws Exception {        Map<String, IntBinaryOperator> change = new HashMap<>();        change.put("add", (x, k) -> x + k);        change.put("mul", (x, k) -> x * k);        change.put("mod", Math::floorMod); // %는 음수에서 음수가 나온다. floorMod는 0 이상 k 미만        Map<String, BiPredicate<Integer, Integer>> keep = new HashMap<>();        keep.put("gt", (x, k) -> x > k);        keep.put("lt", (x, k) -> x < k);        keep.put("div", (x, k) -> x % k == 0);         BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        in.readLine();        List<Integer> a = new ArrayList<>();        StringTokenizer st = new StringTokenizer(in.readLine());        while (st.hasMoreTokens()) a.add(Integer.parseInt(st.nextToken()));        int s = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int i = 0; i < s; i++) {            st = new StringTokenizer(in.readLine());            String name = st.nextToken();            int k = Integer.parseInt(st.nextToken());            a = change.containsKey(name) ? changeAll(a, change.get(name), k) : keepOnly(a, keep.get(name), k);            if (a.isEmpty()) {                out.append("-\n");                continue;            }            for (int j = 0; j < a.size(); j++) out.append(j > 0 ? " " : "").append(a.get(j));            out.append('\n');        }        System.out.print(out);    }}
C#Main.cs · 50 行
using System;using System.Collections.Generic;using System.Text; class Program{    // 함수를 값으로 받는 함수 두 개    static List<int> ChangeAll(List<int> a, Func<int, int, int> rule, int k)    {        var b = new List<int>();        foreach (int x in a) b.Add(rule(x, k));        return b;    }     static List<int> KeepOnly(List<int> a, Func<int, int, bool> rule, int k)    {        var b = new List<int>();        foreach (int x in a) if (rule(x, k)) b.Add(x);        return b;    }     static void Main()    {        var change = new Dictionary<string, Func<int, int, int>>        {            ["add"] = (x, k) => x + k,            ["mul"] = (x, k) => x * k,            ["mod"] = (x, k) => ((x % k) + k) % k, // %는 음수에서 음수가 나온다        };        var keep = new Dictionary<string, Func<int, int, bool>>        {            ["gt"] = (x, k) => x > k,            ["lt"] = (x, k) => x < k,            ["div"] = (x, k) => x % k == 0,        };        Console.ReadLine();        var a = new List<int>();        foreach (string t in Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries)) a.Add(int.Parse(t));        int s = int.Parse(Console.ReadLine());        var sb = new StringBuilder();        for (int i = 0; i < s; i++)        {            string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            int k = int.Parse(p[1]);            a = change.TryGetValue(p[0], out var f) ? ChangeAll(a, f, k) : KeepOnly(a, keep[p[0]], k);            sb.Append(a.Count > 0 ? string.Join(" ", a) : "-").Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 48 行
#include <functional>#include <iostream>#include <map>#include <string>#include <vector> using Change = std::function<int(int, int)>;using Keep = std::function<bool(int, int)>; // 함수를 값으로 받는 함수 두 개std::vector<int> changeAll(const std::vector<int> &a, const Change &rule, int k) {    std::vector<int> b;    for (int x : a) b.push_back(rule(x, k));    return b;} std::vector<int> keepOnly(const std::vector<int> &a, const Keep &rule, int k) {    std::vector<int> b;    for (int x : a) if (rule(x, k)) b.push_back(x);    return b;} int main() {    const std::map<std::string, Change> change = {        {"add", [](int x, int k) { return x + k; }},        {"mul", [](int x, int k) { return x * k; }},        {"mod", [](int x, int k) { return (x % k + k) % k; }}, // %는 음수에서 음수가 나온다    };    const std::map<std::string, Keep> keep = {        {"gt", [](int x, int k) { return x > k; }},        {"lt", [](int x, int k) { return x < k; }},        {"div", [](int x, int k) { return x % k == 0; }},    };    int n, s, k;    std::cin >> n;    std::vector<int> a(n);    for (int &x : a) std::cin >> x;    std::cin >> s;    std::string name;    for (int i = 0; i < s; i++) {        std::cin >> name >> k;        auto it = change.find(name);        a = it != change.end() ? changeAll(a, it->second, k) : keepOnly(a, keep.at(name), k);        if (a.empty()) std::cout << '-';        for (std::size_t j = 0; j < a.size(); j++) std::cout << (j ? " " : "") << a[j];        std::cout << '\n';    }}
Cmain.c · 46 行
#include <stdio.h>#include <string.h> /* 모든 수를 바꾸는 규칙과 남길지 정하는 규칙. 함수 포인터로 넘긴다 */int add(int x, int k) { return x + k; }int mul(int x, int k) { return x * k; }int mod(int x, int k) { return (x % k + k) % k; } /* C의 %는 음수에서 음수가 나온다 */int gt(int x, int k) { return x > k; }int lt(int x, int k) { return x < k; }int divisible(int x, int k) { return x % k == 0; } /* 함수를 값으로 받는 함수 두 개. 남긴 개수를 돌려준다 */int change_all(int *a, int n, int (*rule)(int, int), int k) {    for (int i = 0; i < n; i++) a[i] = rule(a[i], k);    return n;} int keep_only(int *a, int n, int (*rule)(int, int), int k) {    int m = 0;    for (int i = 0; i < n; i++) {        if (rule(a[i], k)) a[m++] = a[i]; /* 앞으로 당겨 담는다 */    }    return m;} int main(void) {    struct { const char *name; int (*fn)(int, int); int changes; } rules[] = {        {"add", add, 1}, {"mul", mul, 1}, {"mod", mod, 1},        {"gt", gt, 0}, {"lt", lt, 0}, {"div", divisible, 0},    };    int n, s, k, a[300];    char name[8];    scanf("%d", &n);    for (int i = 0; i < n; i++) scanf("%d", &a[i]);    scanf("%d", &s);    for (int step = 0; step < s; step++) {        scanf("%7s %d", name, &k);        for (int r = 0; r < 6; r++) {            if (strcmp(rules[r].name, name) != 0) continue;            n = rules[r].changes ? change_all(a, n, rules[r].fn, k) : keep_only(a, n, rules[r].fn, k);        }        if (n == 0) puts("-");        for (int i = 0; i < n; i++) printf(i + 1 < n ? "%d " : "%d\n", a[i]);    }    return 0;}
Craykonmain.cray · 90 行
// 모든 수를 바꾸는 규칙(x, k → 새 수)더하기(x, k){    返回 x + k;} 곱하기(x, k){    返回 x * k;} 나머지(x, k){    // 크레이콘의 %는 음수에서 음수가 나오니 k를 더해 한 번 더 나눈다    返回 (x % k + k) % k;} // 남길지 정하는 규칙(x, k → 참·거짓)큰것만(x, k){    返回 x > k;} 작은것만(x, k){    返回 x < k;} 배수만(x, k){    返回 x % k == 0;} // 함수를 값으로 받는 함수 두 개. 크레이콘에는 람다가 없으니 이름으로 넘긴다모두바꾸기(a, 규칙, k){    b = [];    遍历 (x 在 a)    {        b.添加(규칙(x, k));    }    返回 b;} 골라남기기(a, 규칙, k){    b = [];    遍历 (x 在 a)    {        如果 (규칙(x, k))        {            b.添加(x);        }    }    返回 b;} change = 字典();change["add"] = 더하기;change["mul"] = 곱하기;change["mod"] = 나머지;keep = 字典();keep["gt"] = 큰것만;keep["lt"] = 작은것만;keep["div"] = 배수만; 控制台.读行();a = 控制台.读数字();s = 控制台.读行().转数字();循环 (i = 0; i < s; i++){    p = 控制台.读行().分割();    k = p[1].转数字();    如果 (change.包含(p[0]))    {        a = 모두바꾸기(a, change[p[0]], k);    }    否则    {        a = 골라남기기(a, keep[p[0]], k);    }    如果 (a.数量 == 0)    {        控制台.输出("-");    }    否则    {        控制台.输出(a.连接(" "));    }}