{ }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 はラムダを受け取る関数型インターフェースが必要なので IntBinaryOperator、BiPredicate<Integer, Integer> を使い、C# は Func<int, int, int>、Func<int, int, bool> を使います。
C は関数ポインタ int (*rule)(int, int) で渡し、C++ は std::function<int(int, int)> にラムダを入れます。
Craykon にはラムダがないので、足す(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.つなぐ(" "));    }}