{ }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()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 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·크레이콘은 타입을 적지 않고 그냥 넘겨요.
이름 없는 함수(람다)를 그 자리에서 만드는 법도 달라요: Python lambda w: len(w), JS·TS·C# w => w.length, Java w -> w.length(), C++ [](const std::string& w) { return (int)w.size(); }.
크레이콘과 C에는 람다가 없어서 이름 있는 함수를 만들어 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));}