{ }codelper
高阶函数P0318

接收标准函数,选出第一名的单词

有 n 个单词,还有四种把一个单词变成数的标准。
len 是单词的字母个数。
vowels 是单词中元音 a、e、i、o、u 的个数。
kinds 是单词中用到的不同字母的种数。
last 是单词最后一个字母在字母表中排第几(a 是 1,z 是 26)。
只写一个函数,它接收单词列表和一个标准函数,返回按该标准算出的值最大的单词,并把它用于全部四种标准。
如果值最大的单词有多个,选输入中最先出现的那个。
第一行给出 n 和提问数 q,用一个空格隔开。
第二行给出 n 个单词,用一个空格隔开。
接下来的 q 行每行给出一个标准名。
对每个提问,把选出的单词和它的值用一个空格隔开输出在一行。

输入
1 ≤ n ≤ 100,1 ≤ q ≤ 100。
单词是只由英文小写字母组成的 1 到 20 个字符的字符串,同一个单词可能出现多次。
标准名是 len、vowels、kinds、last 之一
输出
q 行:每个提问选出的单词和它的值
样例 1 · stdin
6 4
kiwi banana fig queue papaya jazz
len
vowels
kinds
last
stdout
banana 6
queue 4
kiwi 3
jazz 26
main.py
输入(stdin)

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

$ 就绪

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

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

这道题是把函数当作值传给另一个函数。
传的时候名字后面不加括号。
Python 有接收标准函数的内置函数 max(words, key=count_vowels),C# 也有 words.MaxBy(key)。
两者在值相同时都返回先出现的那个。
存放函数的类型各语言不同:TS type Key = (w: string) => number,Java ToIntFunction<String>(用 Main::countVowels 指向方法,用 key.applyAsInt(w) 调用),C# Func<string, int>,C 是函数指针 int (*key)(const char *),C++ std::function<int(const std::string&)>。
Python、JS、Craykon 不写类型,直接传。
当场写出没有名字的函数(lambda)的写法也不同:Python lambda w: len(w),JS·TS·C# w => w.length,Java w -> w.length(),C++ [](const std::string& w) { return (int)w.size(); }。
Craykon 和 C 没有 lambda,所以要定义有名字的函数,再像 keys["len"] = 字母数 这样只写名字存进去。
Java Collections.max(words, Comparator.comparingInt(key)) 和 C++ std::max_element 接收比较函数,值相同时返回先出现的那个。
三个陷阱:① 用循环查找时如果用 >= 比较,每遇到值相同的单词都会替换,最后留下的是最后一个(示例的 len 变成 papaya 6,kinds 变成 jazz 3)。
要用 >。
② 排序后取末尾的 Python sorted(words, key=key)[-1] 也会给出相同值中最后的单词,因为排序会保持值相同的元素原来的顺序。
C++ std::minmax_element(...).second 也指向最大值中最后的那个。
③ 在 JS 中像 best(words, key(words[0])) 这样加了括号,传过去的就不是函数,而是当场调用的结果(一个数)。
best 里要调用这个数时,会因 key is not a function 错误而停止。

Pythonmain.py · 27 行
def count_vowels(w):    return sum(1 for c in w if c in "aeiou")  def count_kinds(w):    return len(set(w))  def last_rank(w):    return ord(w[-1]) - ord("a") + 1  # 기준 이름으로 함수를 찾는 표. 이름 뒤에 괄호를 붙이지 않는다(부르지 않고 함수 자체를 담는다)KEYS = {"len": len, "vowels": count_vowels, "kinds": count_kinds, "last": last_rank}  def best(words, key):    # max는 key 함수로 잰 값이 가장 큰 것을 돌려주고, 값이 같으면 먼저 나온 것을 돌려준다    return max(words, key=key)  n, q = map(int, input().split())words = input().split()for _ in range(q):    key = KEYS[input().strip()]    w = best(words, key)    print(w, key(w))
JavaScriptmain.js · 28 行
const lines = require("fs").readFileSync(0, "utf8").split("\n"); // 기준 이름으로 함수를 찾는 표. 화살표 함수는 그대로 값이다const KEYS = {  len: (w) => w.length,  vowels: (w) => [...w].filter((c) => "aeiou".includes(c)).length,  kinds: (w) => new Set(w).size,  last: (w) => w.charCodeAt(w.length - 1) - "a".charCodeAt(0) + 1,}; // 낱말 목록과 기준 함수를 받는다. 더 클 때만(>) 바꾸므로 값이 같으면 먼저 나온 낱말이 남는다function best(words, key) {  let top = words[0];  for (const w of words) {    if (key(w) > key(top)) top = w;  }  return top;} const [n, q] = lines[0].trim().split(/\s+/).map(Number);const words = lines[1].trim().split(/\s+/);const out = [];for (let i = 0; i < q; i++) {  const key = KEYS[lines[2 + i].trim()];  const w = best(words, key);  out.push(w + " " + key(w));}console.log(out.join("\n"));
TypeScriptmain.ts · 32 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n"); // 함수의 타입: 낱말을 받아 수를 돌려준다type Key = (w: string) => number; // 기준 이름으로 함수를 찾는 표. 화살표 함수는 그대로 값이다const KEYS: Record<string, Key> = {  len: (w) => w.length,  vowels: (w) => [...w].filter((c) => "aeiou".includes(c)).length,  kinds: (w) => new Set(w).size,  last: (w) => w.charCodeAt(w.length - 1) - "a".charCodeAt(0) + 1,}; // 낱말 목록과 기준 함수를 받는다. 더 클 때만(>) 바꾸므로 값이 같으면 먼저 나온 낱말이 남는다function best(words: string[], key: Key): string {  let top: string = words[0];  for (const w of words) {    if (key(w) > key(top)) top = w;  }  return top;} const [n, q]: number[] = lines[0].trim().split(/\s+/).map(Number);const words: string[] = lines[1].trim().split(/\s+/);const out: string[] = [];for (let i = 0; i < q; i++) {  const key: Key = KEYS[lines[2 + i].trim()];  const w: string = best(words, key);  out.push(w + " " + key(w));}console.log(out.join("\n"));
JavaMain.java · 55 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.Arrays;import java.util.Collections;import java.util.Comparator;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.StringTokenizer;import java.util.function.ToIntFunction; public class Main {    static int countVowels(String w) {        int count = 0;        for (char c : w.toCharArray()) if ("aeiou".indexOf(c) >= 0) count++;        return count;    }     static int countKinds(String w) {        return (int) w.chars().distinct().count();    }     static int lastRank(String w) {        return w.charAt(w.length() - 1) - 'a' + 1;    }     // 낱말 목록과 기준 함수를 받는다. Collections.max는 값이 같으면 먼저 나온 것을 돌려준다    static String best(List<String> words, ToIntFunction<String> key) {        return Collections.max(words, Comparator.comparingInt(key));    }     public static void main(String[] args) throws IOException {        // 함수를 값으로 담는 타입이 ToIntFunction<String>이다. 메서드는 "클래스::이름"으로 가리킨다        Map<String, ToIntFunction<String>> keys = new HashMap<>();        keys.put("len", String::length);        keys.put("vowels", Main::countVowels);        keys.put("kinds", Main::countKinds);        keys.put("last", Main::lastRank);         BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(in.readLine());        int n = Integer.parseInt(st.nextToken());        int q = Integer.parseInt(st.nextToken());        List<String> words = Arrays.asList(in.readLine().trim().split(" +"));        StringBuilder out = new StringBuilder();        for (int i = 0; i < q; i++) {            ToIntFunction<String> key = keys.get(in.readLine().trim());            String w = best(words, key);            // 담아 둔 함수는 key(w)가 아니라 key.applyAsInt(w)로 부른다            out.append(w).append(' ').append(key.applyAsInt(w)).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 36 行
using System;using System.Collections.Generic;using System.Linq;using System.Text; class Program{    static int CountVowels(string w) => w.Count(c => "aeiou".Contains(c));    static int CountKinds(string w) => w.Distinct().Count();    static int LastRank(string w) => w[w.Length - 1] - 'a' + 1;     // 낱말 목록과 기준 함수를 받는다. MaxBy는 값이 같으면 먼저 나온 것을 돌려준다    static string Best(string[] words, Func<string, int> key) => words.MaxBy(key);     static void Main()    {        // 함수를 값으로 담는 타입이 Func<string, int>이다(string을 받아 int를 돌려주는 함수)        var keys = new Dictionary<string, Func<string, int>>        {            ["len"] = w => w.Length,            ["vowels"] = CountVowels,            ["kinds"] = CountKinds,            ["last"] = LastRank,        };        int q = int.Parse(Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries)[1]);        string[] words = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        var sb = new StringBuilder();        for (int i = 0; i < q; i++)        {            Func<string, int> key = keys[Console.ReadLine().Trim()];            string best = Best(words, key);            sb.Append(best).Append(' ').Append(key(best)).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 39 行
#include <algorithm>#include <functional>#include <iostream>#include <map>#include <set>#include <string>#include <vector> // 함수를 값으로 담는 타입: 낱말을 받아 int를 돌려주는 것이면 함수든 람다든 담긴다using Key = std::function<int(const std::string&)>; // 낱말 목록과 기준 함수를 받는다. max_element는 값이 같으면 먼저 나온 것을 가리킨다const std::string& best(const std::vector<std::string>& words, const Key& key) {    return *std::max_element(words.begin(), words.end(),                             [&](const std::string& a, const std::string& b) { return key(a) < key(b); });} int main() {    std::map<std::string, Key> keys = {        {"len", [](const std::string& w) { return (int)w.size(); }},        {"vowels", [](const std::string& w) {             return (int)std::count_if(w.begin(), w.end(), [](char c) { return std::string("aeiou").find(c) != std::string::npos; });         }},        {"kinds", [](const std::string& w) { return (int)std::set<char>(w.begin(), w.end()).size(); }},        {"last", [](const std::string& w) { return w.back() - 'a' + 1; }},    };     int n, q;    std::cin >> n >> q;    std::vector<std::string> words(n);    for (auto& w : words) std::cin >> w;    for (int i = 0; i < q; i++) {        std::string name;        std::cin >> name;        const Key& key = keys[name];        const std::string& w = best(words, key);        std::cout << w << ' ' << key(w) << '\n';    }}
Cmain.c · 61 行
#include <stdio.h>#include <string.h> static int length_of(const char *w) {    return (int)strlen(w);} static int count_vowels(const char *w) {    int count = 0;    for (; *w; w++) if (strchr("aeiou", *w)) count++;    return count;} static int count_kinds(const char *w) {    int seen[26] = {0}, count = 0;    for (; *w; w++) {        if (!seen[*w - 'a']) {            seen[*w - 'a'] = 1;            count++;        }    }    return count;} static int last_rank(const char *w) {    return w[strlen(w) - 1] - 'a' + 1;} /* 함수 포인터 타입: 낱말을 받아 int를 돌려주는 함수를 가리킨다 */typedef int (*key_fn)(const char *); /* 낱말 목록과 기준 함수를 받는다. 더 클 때만(>) 바꾸므로 값이 같으면 먼저 나온 낱말이 남는다 */static const char *best(char words[][21], int n, key_fn key) {    const char *top = words[0];    for (int i = 1; i < n; i++) {        if (key(words[i]) > key(top)) top = words[i];    }    return top;} int main(void) {    /* 기준 이름과 함수를 짝지은 표. 함수 이름만 쓰면 그 함수의 주소다 */    const char *names[] = {"len", "vowels", "kinds", "last"};    key_fn keys[] = {length_of, count_vowels, count_kinds, last_rank};     int n, q;    static char words[100][21];    scanf("%d %d", &n, &q);    for (int i = 0; i < n; i++) scanf("%20s", words[i]);    for (int i = 0; i < q; i++) {        char name[16];        scanf("%15s", name);        for (int k = 0; k < 4; k++) {            if (strcmp(name, names[k]) == 0) {                const char *w = best(words, n, keys[k]);                printf("%s %d\n", w, keys[k](w));            }        }    }    return 0;}
Craykonmain.cray · 64 行
// 기준 함수 네 개. 크레이콘에는 람다가 없어서 이름 있는 함수로 만든다글자수(w){    返回 w.长度;} 모음수(w){    count = 0;    遍历 (c 在 w)    {        如果 ("aeiou".包含(c))        {            count++;        }    }    返回 count;} 가짓수(w){    본것 = 字典();    遍历 (c 在 w)    {        본것[c] = 真;    }    返回 본것.数量;} 끝글자순번(w){    返回 字符.编码(w[w.长度 - 1]) - 字符.编码("a") + 1;} // 낱말 목록과 기준 함수를 받는다. 더 클 때만(>) 바꾸므로 값이 같으면 먼저 나온 낱말이 남는다으뜸(낱말들, 기준){    top = 낱말들[0];    遍历 (w 在 낱말들)    {        如果 (기준(w) > 기준(top))        {            top = w;        }    }    返回 top;} // 함수 이름을 괄호 없이 쓰면 함수 자체가 값으로 담긴다keys = 字典();keys["len"] = 글자수;keys["vowels"] = 모음수;keys["kinds"] = 가짓수;keys["last"] = 끝글자순번; 첫줄 = 控制台.读数字();q = 첫줄[1];words = 控制台.读行().去空白().分割();循环 (i = 0; i < q; i++){    key = keys[控制台.读行().去空白()];    w = 으뜸(words, key);    控制台.输出(w + " " + key(w));}