함수 · 15
함수 더 알아보기
변수 범위, 여러 값 돌려주기, 함수를 값으로 넘기기.
함수를 한 걸음 더 쓰는 세 가지예요: 변수가 어디에 사는지(범위), 값을 둘 돌려주는 법, 함수를 값처럼 넘기는 법.
아래 예제는 수들에서 가장 작은 값과 가장 큰 값을 한 번에 돌려받고, 조건 함수를 넘겨 짝수와 10 이상인 수를 세고, 조건을 모두 몇 번 확인했는지를 함수 밖 변수에 적어요.
checks = 0 # 함수 밖 변수: 프로그램이 끝날 때까지 남아요 def is_even(x): return x % 2 == 0 def is_big(x): return x >= 10 def min_max(nums): lo = hi = nums[0] for x in nums: lo = min(lo, x) hi = max(hi, x) return lo, hi # 두 값을 함께 돌려줘요 def count_if(nums, test): # test에는 함수가 들어와요 global checks # 밖의 변수를 바꾸겠다고 알려요 count = 0 # 함수 안 변수: 부를 때마다 새로 만들어져요 for x in nums: checks += 1 if test(x): count += 1 return count nums = list(map(int, input().split()))lo, hi = min_max(nums)print(lo, hi)print(count_if(nums, is_even))print(count_if(nums, is_big))print(checks)let checks = 0; // 함수 밖 변수: 프로그램이 끝날 때까지 남아요 function isEven(x) { return x % 2 === 0;} function isBig(x) { return x >= 10;} function minMax(nums) { let lo = nums[0], hi = nums[0]; for (const x of nums) { lo = Math.min(lo, x); hi = Math.max(hi, x); } return [lo, hi]; // 두 값을 배열에 담아 돌려줘요} function countIf(nums, test) { // test에는 함수가 들어와요 let count = 0; // 함수 안 변수: 부를 때마다 새로 만들어져요 for (const x of nums) { checks++; // 밖의 변수를 바꿔요 if (test(x)) count++; } return count;} const nums = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [lo, hi] = minMax(nums);console.log(lo + " " + hi);console.log(countIf(nums, isEven));console.log(countIf(nums, isBig));console.log(checks);import { readFileSync } from "node:fs"; let checks = 0; // 함수 밖 변수: 프로그램이 끝날 때까지 남아요 function isEven(x: number): boolean { return x % 2 === 0;} function isBig(x: number): boolean { return x >= 10;} function minMax(nums: number[]): [number, number] { let lo = nums[0], hi = nums[0]; for (const x of nums) { lo = Math.min(lo, x); hi = Math.max(hi, x); } return [lo, hi]; // 두 값을 배열(튜플)에 담아 돌려줘요} function countIf(nums: number[], test: (x: number) => boolean): number { // test에는 함수가 들어와요 let count = 0; // 함수 안 변수: 부를 때마다 새로 만들어져요 for (const x of nums) { checks++; // 밖의 변수를 바꿔요 if (test(x)) count++; } return count;} const nums: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [lo, hi] = minMax(nums);console.log(lo + " " + hi);console.log(countIf(nums, isEven));console.log(countIf(nums, isBig));console.log(checks);import java.util.*;import java.util.function.IntPredicate; public class Main { static int checks = 0; // 함수 밖 변수(클래스의 static 필드): 프로그램이 끝날 때까지 남아요 static boolean isEven(int x) { return x % 2 == 0; } static boolean isBig(int x) { return x >= 10; } static int[] minMax(List<Integer> nums) { int lo = nums.get(0), hi = nums.get(0); for (int x : nums) { lo = Math.min(lo, x); hi = Math.max(hi, x); } return new int[] {lo, hi}; // 두 값을 배열에 담아 돌려줘요 } static int countIf(List<Integer> nums, IntPredicate test) { // test에는 함수가 들어와요 int count = 0; // 함수 안 변수: 부를 때마다 새로 만들어져요 for (int x : nums) { checks++; // 밖의 변수를 바꿔요 if (test.test(x)) count++; } return count; } public static void main(String[] args) { Scanner in = new Scanner(System.in); List<Integer> nums = new ArrayList<>(); while (in.hasNextInt()) nums.add(in.nextInt()); int[] r = minMax(nums); int lo = r[0], hi = r[1]; System.out.println(lo + " " + hi); System.out.println(countIf(nums, Main::isEven)); System.out.println(countIf(nums, Main::isBig)); System.out.println(checks); }}using System;using System.Linq; class Program{ static int checks = 0; // 함수 밖 변수(클래스의 static 필드): 프로그램이 끝날 때까지 남아요 static bool IsEven(int x) { return x % 2 == 0; } static bool IsBig(int x) { return x >= 10; } static (int, int) MinMax(int[] nums) { int lo = nums[0], hi = nums[0]; foreach (int x in nums) { lo = Math.Min(lo, x); hi = Math.Max(hi, x); } return (lo, hi); // 두 값을 튜플로 함께 돌려줘요 } static int CountIf(int[] nums, Func<int, bool> test) // test에는 함수가 들어와요 { int count = 0; // 함수 안 변수: 부를 때마다 새로 만들어져요 foreach (int x in nums) { checks++; // 밖의 변수를 바꿔요 if (test(x)) count++; } return count; } static void Main() { int[] nums = Console.ReadLine().Split().Select(int.Parse).ToArray(); var (lo, hi) = MinMax(nums); Console.WriteLine(lo + " " + hi); Console.WriteLine(CountIf(nums, IsEven)); Console.WriteLine(CountIf(nums, IsBig)); Console.WriteLine(checks); }}#include <algorithm>#include <functional>#include <iostream>#include <utility>#include <vector> int checks = 0; // 함수 밖 변수(전역 변수): 프로그램이 끝날 때까지 남아요 bool isEven(int x) { return x % 2 == 0;} bool isBig(int x) { return x >= 10;} std::pair<int, int> minMax(const std::vector<int>& nums) { int lo = nums[0], hi = nums[0]; for (int x : nums) { lo = std::min(lo, x); hi = std::max(hi, x); } return {lo, hi}; // 두 값을 pair에 담아 돌려줘요} int countIf(const std::vector<int>& nums, const std::function<bool(int)>& test) { // test에는 함수가 들어와요 int count = 0; // 함수 안 변수: 부를 때마다 새로 만들어져요 for (int x : nums) { checks++; // 밖의 변수를 바꿔요 if (test(x)) count++; } return count;} int main() { std::vector<int> nums; int v; while (std::cin >> v) nums.push_back(v); auto [lo, hi] = minMax(nums); std::cout << lo << ' ' << hi << '\n'; std::cout << countIf(nums, isEven) << '\n'; std::cout << countIf(nums, isBig) << '\n'; std::cout << checks << '\n';}#include <stdio.h> int checks = 0; // 함수 밖 변수(전역 변수): 프로그램이 끝날 때까지 남아요 int is_even(int x) { return x % 2 == 0;} int is_big(int x) { return x >= 10;} // 돌려줄 값이 둘이라, 결과를 적어 줄 자리(주소)를 받아요void min_max(const int *nums, int n, int *lo, int *hi) { *lo = *hi = nums[0]; for (int i = 0; i < n; i++) { if (nums[i] < *lo) *lo = nums[i]; if (nums[i] > *hi) *hi = nums[i]; }} int count_if(const int *nums, int n, int (*test)(int)) { // test에는 함수(의 주소)가 들어와요 int count = 0; // 함수 안 변수: 부를 때마다 새로 만들어져요 for (int i = 0; i < n; i++) { checks++; // 밖의 변수를 바꿔요 if (test(nums[i])) count++; } return count;} int main(void) { int nums[100], n = 0; while (n < 100 && scanf("%d", &nums[n]) == 1) n++; int lo, hi; min_max(nums, n, &lo, &hi); printf("%d %d\n", lo, hi); printf("%d\n", count_if(nums, n, is_even)); printf("%d\n", count_if(nums, n, is_big)); printf("%d\n", checks); return 0;}checks = 0; // 함수 밖(맨 위) 변수: 함수 안에서도 같은 변수예요 isEven(x){ 돌려주기 x % 2 == 0;} isBig(x){ 돌려주기 x >= 10;} minMax(nums){ lo = nums[0]; hi = nums[0]; 각각 (x 에서 nums) { lo = 수학.작은수(lo, x); hi = 수학.큰수(hi, x); } 돌려주기 [lo, hi]; // 두 값을 목록에 담아 돌려줘요} countIf(nums, test){ count = 0; // 함수 안 변수: 부를 때마다 새로 만들어져요 각각 (x 에서 nums) { checks++; // 밖의 변수를 바꿔요 만약 (test(x)) { count++; } } 돌려주기 count;} nums = 콘솔.수들읽기();r = minMax(nums);콘솔.쓰기(r[0] + " " + r[1]);콘솔.쓰기(countIf(nums, isEven));콘솔.쓰기(countIf(nums, isBig));콘솔.쓰기(checks);4 15 8 23 6 7
4 23 3 2 12
핵심
- 변수에는 사는 곳(범위)이 있어요.
함수 안에서 만든 변수는 그 함수 안에서만 쓰이고, 부를 때마다 새로 만들어져요.
예제의count는 부를 때마다 0에서 다시 시작해요. - 함수 밖에서 만든 변수는 프로그램이 끝날 때까지 남아요.
예제의checks는 함수를 두 번 부르는 동안 값이 이어져서 12가 돼요. - 함수 안에서 밖의 변수를 바꾸는 방법은 언어마다 달라요.
밖의 변수를 여기저기서 바꾸면 어디서 바뀌었는지 찾기 어려워요.
그래서 예제의checks처럼 프로그램 전체에 하나뿐인 기록에만 쓰고, 필요한 값은 매개변수로 받고count같은 계산 결과는return으로 돌려줘요. - 값을 둘 돌려주려면 한 덩어리로 묶어 돌려주고, 받는 쪽에서 다시 풀어요.
묶는 그릇(튜플, 배열, pair, 목록)이 언어마다 달라요.
C는 결과를 적을 주소를 받아요. - 함수도 값이라서 다른 함수에 넘길 수 있어요.
예제는 세는 함수 하나로 "짝수 세기"와 "10 이상 세기"를 모두 해요.
달라지는 부분(조건)만 함수로 빼서 넘기는 거예요. - 넘길 때는 괄호 없이 이름만 써요.
is_even은 함수 자체이고,is_even(4)는 함수를 불러서 나온 결과예요.
언어별로 쓰는 법
| Python | 함수 안에서 밖의 변수를 읽기만 할 때는 그냥 쓰고, 바꿀 때는 함수 첫머리에 global checks를 적어요.두 값은 return lo, hi(튜플)로 돌려주고 lo, hi = min_max(nums)로 받아요.함수는 count_if(nums, is_even)처럼 이름만 넘기고, 짧은 조건은 lambda x: x >= 10으로 그 자리에서 만들어요 |
|---|---|
| JavaScript | 함수 밖에서 let으로 만든 변수는 함수 안에서 표시 없이 읽고 바꿔요.함수 안에서 let으로 같은 이름을 또 만들면 밖의 것과 다른 변수예요.두 값은 return [lo, hi];로 돌려주고 const [lo, hi] = minMax(nums);로 받아요.함수는 countIf(nums, isEven)처럼 넘기고, 짧은 조건은 화살표 함수 (x) => x >= 10 |
| TypeScript | JavaScript와 같아요. 두 값을 돌려주는 타입은 [number, number], 함수를 받는 매개변수는 test: (x: number) => boolean처럼 적어요 |
| Java | 클래스 밖에는 변수를 둘 수 없어서, 함수 밖 변수는 클래스의 static 필드로 둬요(static int checks = 0;).두 값은 int[]에 담거나 record MinMax(int lo, int hi) {}를 만들어 돌려줘요.함수는 IntPredicate test로 받아 test.test(x)로 부르고, 넘길 때는 Main::isEven이나 람다 x -> x >= 10.람다 안에서 main의 지역 변수를 바꾸면 컴파일 오류예요 |
| C# | 클래스 밖에는 변수를 둘 수 없어서, 함수 밖 변수는 클래스의 static 필드로 둬요.두 값은 튜플 (int, int)로 return (lo, hi);, 받을 때는 var (lo, hi) = MinMax(nums);.out 매개변수(out int lo)로 받는 방법도 있어요.함수는 Func<int, bool> test로 받고, 넘길 때는 IsEven이나 람다 x => x >= 10 |
| C++ | 함수 밖에 쓴 변수는 전역 변수라서 어느 함수에서나 읽고 바꿔요. 두 값은 std::pair<int, int>로 return {lo, hi};, 받을 때는 auto [lo, hi] = minMax(nums);.함수는 std::function<bool(int)>(<functional>)로 받고, 넘길 때는 isEven이나 람다 [](int x) { return x >= 10; } |
| C | 함수 밖에 쓴 변수는 전역 변수예요. 값을 둘 돌려주는 문법이 없어서, 예제처럼 결과를 적을 주소를 받거나( int *lo, int *hi, 부를 때는 &lo, &hi) 구조체에 담아 돌려줘요.함수는 함수 포인터 int (*test)(int)로 받고 count_if(nums, n, is_even)처럼 이름을 넘겨요.람다는 없어요 |
| Craykon | 맨 위에서 만든 변수는 함수 안에서도 같은 변수라서 표시 없이 읽고 바꿔요. 함수 안에서 처음 만든 변수는 그 함수 안에서만 쓰여요. 두 값은 돌려주기 [lo, hi];처럼 목록에 담아 돌려주고 r[0], r[1]로 꺼내요.람다가 없어서 이름 있는 함수를 countIf(nums, isEven)처럼 넘겨요 |
자주 하는 실수
함수 안 변수 이름이 맨 위 변수와 같았어요Craykon
total = 100;
twice(x)
{
total = x * 2;
돌려주기 total;
}
콘솔.쓰기(twice(4));
콘솔.쓰기(total);→ 8, 8 — 함수가 새 변수를 만든 게 아니라 맨 위의 total을 바꿔서, 100이 사라졌어요
함수 안에서만 쓸 변수는 맨 위 변수와 다른 이름으로 지어요.
매개변수는 이름이 같아도 그 함수만의 변수예요.
다른 언어에서는 함수 안에서 let total·int total처럼 새로 만들면(Python은 global 없이 값을 넣으면) 밖의 것과 다른 변수라서 8, 100이 나와요.
global 없이 밖의 변수를 바꿨어요Python
checks = 0
def check(x):
checks += 1
return x % 2 == 0
print(check(4))→ UnboundLocalError: cannot access local variable 'checks' where it is not associated with a value — 함수 안에서 값을 넣는 이름은 그 함수의 변수로 봐요. 그래서 아직 값이 없는 checks에 1을 더하려다 멈춰요
함수 첫머리에 global checks를 적어요.
읽기만 할 때는 필요 없어요.
return lo, hi;로 두 값을 돌려주려 했어요JavaScript · C · C++
function minMax(nums) {
let lo = nums[0], hi = nums[0];
for (const x of nums) { lo = Math.min(lo, x); hi = Math.max(hi, x); }
return lo, hi;
}
console.log(minMax([4, 15, 8]));→ 15 — 쉼표 연산자는 뒤의 값만 남겨서 hi만 돌아와요. C·C++도 경고만 하고 컴파일돼서 똑같이 15예요. TypeScript는 타입 검사에서 오류(TS2695)로 막아 줘요
JavaScript는 return [lo, hi];, C++는 std::pair로 return {lo, hi};, C는 주소를 받거나 구조체로 돌려줘요.return lo, hi가 두 값을 돌려주는 건 Python이에요.
함수를 넘길 때
countIf(nums, isEven())처럼 괄호를 붙이면 넘기는 게 아니라 그 자리에서 부르는 거예요.JavaScript는
TypeError: test is not a function, Python은 TypeError: is_even() missing 1 required positional argument: 'x'로 멈춰요.TypeScript는 타입 검사에서, 크레이콘은 "isEven에는 값이 1개 필요한데 0개를 넣었어요"라는 오류로 알려 줘요.