본 글의 저작권과 원문은 https://blog.naver.com/sweetie_rex 에 있습니다.
현재의 글은 업데이트가 되지 않음을 유의해서 읽어주세요.
최신 업데이트 된 글을 읽으시려면 아래의 링크를 확인해주세요.

함수 (Function)

프로그래밍을 처음으로 접해본다면, 함수라는 단어는 수학에서 자주 들어봤을 거에요.
1차함수, 2차함수 이런거 있잖아요. 수학에서의 함수와 프로그래밍에서의 함수는 그 의미가 같아요.
그렇지만 수학을 못한다고 해서 프로그래밍을 어렵게 생각할 필요는 없어요. 프로그래밍은 수식에 대한 이해가 없어도, 논리에 대한 이해만 있으면 누구나 할 수 있어요.
프로그래밍은 논리적 사고능력이 중요한거지, 수식을 풀어내는 능력이 중요한 것은 아니에요.

위키백과에 함수에 대한 적절한 그림이 있어서 가져왔어요.

함수를 간단히 표현하면 '뭔가를 집어넣으면, 그에 대한 결과값이 나오는 것' 이라고 말할 수 있어요. 프로그래밍은 인생이니까, 일상생활에서 예를 찾으면 아주 쉬워요.
예를 들면, 음료수 자판기나 사다리타기 게임도 '함수' 라고 할 수 있어요.

우리는 '자판기' 라는 함수에 1,000원과 원하는 번호를 입력하면 '삼다수' 라는 결과값을 얻을 수 있죠.

함수의 선언과 실행 방법

Python 에서 함수를 정의(definition) 하는 방법을 알아볼게요.

def 함수이름(변수1, 변수2, ..., 변수n):  # 함수의 정의, 입력을 받을 매개변수(parameter)를 설정함
    ...  # 로직(logic, 논리, 알고리즘)
    return 함수의결과값

결과값 = 함수이름(변수1, 변수2, ..., 변수n)  # 정의된 함수를 실행하여 결과값을 받음

파이썬에서 함수를 정의할때는 def 라는 키워드를 사용해요.
그리고 함수 이름을 적고, 함수에서 사용할 매개변수(parameter) 값들을 작성한 뒤, 함수에서 필요한 로직을 작성하고, 결과값을 return 하는 구조에요.
모든 함수는 return 이라는 키워드를 만나는 즉시 값을 반환하면서 종료 돼요.
만약 return 키워드 옆에 아무것도 입력하지 않는다면, 아무런 값도 반환하지 않고 종료하게 되죠.
이제 위의 그림을 다시 보면 이해가 될거에요.

※ 매개변수(parameter) 라는 건, 함수의 매개체가 되는 변수를 말해요.

함수에 꼭 파라미터가 있어야 하는것은 아니에요.
아주 간단히 함수를 선언해볼게요.

def greeting():  # 입력받는 파라미터가 없음
    print('Hello, my first Function!')
    변수1 = 20 + 30
    print('더하기 결과: ', 변수1)
    return  # 리턴값도 없음

greeting()  # 리턴값이 없으니 따로 특졍 변수에 할당할 필요도 없음

이번에는 입력받는 파라미터도 있고, 결과값도 있는 함수를 선언해볼게요.

def add(num1, num2):  # 입력받는 파라미터가 2개
    print('안녕, 나의 두번째 함수!')
    변수1 = num1 + num2
    print('더하기 결과: ', 변수1)
    return 변수1  # 변수1 을 함수의 결과값으로 설정

함수결과 = add(22, 30)  # 2개의 파라미터를 함수에 전달하고, 리턴값을 받아서 '함수결과' 라는 변수에 할당
print(함수결과)  # 52

함수(Function)와 추상(Abstraction)

조금 더 자세히 설명해볼게요.

어떤 함수에 2를 넣었다고 생각해보죠. 4가 나왔어요.
같은 함수에 3을 넣었고, 6이 나왔어요.
그럼 5를 넣으면 10이 나올거라고 예상이 되죠?

그럼 이 함수는 내부적으로 어떻게 구현 되어있는지는 모르겠지만, 어떤 숫자를 집어넣었을때 곱하기 2를 해서 되돌려주는 행동을 하는것 같아요.

이 함수의 이름을 짓는다면, 곱하기2 또는 multiply_by_2 라고 할 수도 있겠네요? 이게 이전에 이야기 했던 추상화(Abstract)에요. 위의 내용을 곱하기2 또는 multiply_by_2 라고 추상화 한거죠.

이 함수를 코드로 구현해볼게요.

def multiply_by_2(num):  # num 이라는 입력값을 받는 함수 선언
    return num * 2  # 함수의 결과값으로 2를 곱해서 되돌려 줌
    
결과1 = multiply_by_2(2)  # 함수에 2를 넣고 실행한 결과를 '결과1' 변수에 저장
print(결과1)  # 4

결과2 = multiply_by_2(3)  # 함수에 3을 넣고 실행한 결과를 '결과2' 변수에 저장
print(결과2)  # 6

결과3 = multiply_by_2(5)  # 함수에 5를 넣고 실행한 결과를 '결과3' 변수에 저장
print(결과3)  # 10

결과4 = multiply_by_2(1024)  # 함수에 1024를 넣고 실행한 결과를 '결과4' 변수에 저장
print(결과4)  # 2048

완벽한 함수: 자판기

자판기는 완벽한 함수에요.
자판기 라는 함수에 (돈, 상품명) 이라는 값을 넣으면, 그에 맞는 물건을 되돌려주죠.

여기서 이전에 설명했던 추상화 과정이 들어가는데,
생수 를 구매할때 1000 원이 필요하고,
보리차 를 구매할때 1500 원이 필요하고,
우유 를 구매할때 2000 원이 필요하다고 가정해볼게요.

여기에는 공통된 특징이 있어요.
물건구매 할때에는 이 필요하다는 거죠.
그러면 여기에서 변수는 물건 이에요.
구매 하는 행위는 변하지 않죠.

그렇다면 이 공통된 특징들을 모아서 하나의 자판기 라는 함수로 추상화하여 만들면 좀더 규칙적이고 편리하게 이용이 가능하겠지요?
그게 자판기 라는 함수이며, 동시에 자판기 라는 함수로 추상화를 시킨 것이죠.

(돈, 상품명) 이라는 값을 넣으면, 그에 맞는 물건을 되돌려 주는 무언가 를 우리는 자판기 라고 추상화 했다는 거죠.

자판기의 원리를 Python 의 함수로 구현해볼게요. 아주 쉬워요.
코드가 좀 길지만 우리가 여태까지 배운 것들을 모두 써먹어볼거에요.

def 자판기(, 상품명):  # 함수의 정의
    가격표 = {
        '생수': 1000,
        '보리차': 1500,
        '우유': 2000,
    }
    지급할물건 = None  # 변수 초기화
    거스름돈 = 0  # 변수 초기화

    if (상품명 not in 가격표):  # 입력받은 상품이 가격표에 없으면
        print(f'{상품명} 이라는 상품은 없어요. 주신 돈 {}원을 환불해드릴게요.')  # f-string 문법
        return {
            '물건': None,
            '거스름돈':}  # 입력받은 상품이 없으니, 입력받은 돈을 전액 환불

    elif (>= 가격표[상품명]):  # 입력받은 돈이 상품의 가격보다 높거나 같으면
        지급할물건 = 상품명
        거스름돈 =- 가격표[상품명]
        print(f'{지급할물건}을(를) 구매했어요! 거스름돈은 {거스름돈}원 이에요.')
        return {
            '물건': 지급할물건,
            '거스름돈': 거스름돈
        }  # 상품과 거스름돈을 돌려받음

    else:  # 상품이 가격표에 있지만, 돈이 부족하면
        print(f'돈이 부족해요. {상품명}을(를) 구매하기 위해서는 {가격표[상품명] -}원이 더 필요해요.')
        return {
            '물건': 지급할물건,
            '거스름돈': 거스름돈
        }  # 상품과 거스름돈을 돌려받음

결과1 = 자판기(1000, '생수')  # 1000원으로 '생수' 구매 시도
print(f'구매 결과1: {결과1}\n')

결과2 = 자판기(2000, '보리차')  # 2000원으로 '보리차' 구매 시도
print(f'구매 결과2: {결과2}\n')

결과3 = 자판기(2000, '아메리카노')  # 2000원으로 '아메리카노' 구매 시도
print(f'구매 결과3: {결과3}\n')

결과4 = 자판기(3000, '우유')  # 3000원으로 '우유' 구매 시도
print(f'구매 결과4: {결과4}\n')

위 코드를 실행해보고, 함수와 추상화란 무엇인지 생각해보세요.

자판기의 예시가 이해가 되나요?
추상화를 왜 함수 앞에서 설명했는지 이해가 되었다면 좋겠어요.

함수 부연설명

우리가 지하철 앱을 사용할 때, 출발역과 도착역을 입력하면 걸리는 시간, 노선도 등을 얻게되죠?
그것은 input 으로 (출발역, 도착역)을 받고, output 으로 지하철 소요시간 을 리턴하는 함수에요.
그리고 input 으로 (출발역, 도착역)을 받고, output 으로 최적의 노선도 을 리턴하는 함수죠.

그리고 위에서 보여준 예시처럼
3 => 6
4 => 8
5 => 10
이런 함수가 있다고 생각해보면

100 => 200
...
3000 => 6000

모든 계산을 위해서 3000개의 항목을 다 만들 수는 없잖아요?
그냥 함수(숫자) => 숫자 * 2
이 문장 하나로 정리가 되죠.
이게 이 책에서 말하고자 하는 추상화입니다.
이게 곧 함수이고요.

그럼 이번에는 '햄버거' 만드는 것을 예시로 들어볼게.
햄버거의 재료에는 , 양상추, 패티, 슬라이스치즈 가 들어간다고 해보자.
햄버거 만들기 시작!

1. 빵을 두 조각으로 나눈다.
2. 빵 아랫조각을 테이블에 놓는다.
3. 빵 아랫조각 위에 양상추를 올린다.
4. 패티를 굽는다.
5. 양상추 위에 패티를 올린다.
6. 패티 위에 슬라이스치즈를 올린다.
7. 슬라이스치즈 위에 빵 윗조각으로 덮는다.
8. 햄버거 완성!

이것을 함수로 만들어볼게. 문법이 정확하지는 않으니까 대략 이런 느낌이구나 라는것만 봐줘 :-)

def 햄버거_만들기(, 양상추, 패티, 슬라이스치즈):
    윗조각, 아랫조각 =/ 2
    햄버거 = ''  # 함수의 최종 결과로 내보낼 햄버거의 초안 (빈값)
    햄버거 += 아랫조각    # 빵 놓음
    햄버거 += 양상추    # 양상추 올림
    구운패티 = 패티_굽기(패티)  # 아래에 선언된 '패티_굽기' 함수에 패티를 넣고 실행
    햄버거 += 구운패티  # 패티 올림
    햄버거 += 슬라이스치즈  # 치즈 올림
    햄버거 += 윗조각
    return 햄버거  # 결과값으로 햄버거 return

def 패티_굽기(패티):
    구운패티 = 패티 + 2분간 불로 지짐
    return 구운패티  # 결과값으로 구운패티를 return

이게 프로그래밍에서 사용하는 함수(function)의 개념이야.
function 은 '기능' 이라는 뜻이야. 이전에 '추상화(Abstract)' 를 설명할 때, 함수는 우리 인생에 특정 영역의 기능을 추상화(Abstract) 하여 구현해놓은 것 이라고 했어.

수학에서 사용하는 수식예를들면: f(x) = ax + b 보다 훨씬 쉽지?
우리는 햄버거_만들기 라는 함수를 실행하기만 하면 언제든지 무한히 햄버거를 만들어낼 수 있어.

위의 햄버거_만들기 라는 함수와 패티_굽기 라는 함수를 이용해서 햄버거 타이쿤 같은 게임을 만들수도 있을거야!

햄버거1 = 햄버거_만들기(빵, 양상추, 패티, 슬라이스치즈)
햄버거2 = 햄버거_만들기(빵, 양상추, 패티, 슬라이스치즈)
햄버거3 = 햄버거_만들기(빵, 양상추, 패티, 슬라이스치즈)
...
햄버거n = 햄버거_만들기(빵, 양상추, 패티, 슬라이스치즈)

이런게 함수(function)이고, 함수는 프로그래밍의 핵심적인 개념이야.

인생에서 일어나는 반복적인 일들을 자동화, 정형화 시켜놓은 것.

프로그래밍에서의 함수는 결론적으로 이렇게 말할 수 있어.

어떤 행동, 어떤 행위, 어떤 기능을 추상화 시켜놓은 것

위의 사례에서는 다양한 로직들을 햄버거_만들기 라는 하나의 function 으로 추상화(Abstraction) 시켰는데, 프로그래밍에서는 이런 추상화 능력이 상당히 중요해.

하나의 프로그램은 수 많은 함수들의 조합으로 구성된다고 보면 돼.
우리의 인생이 수 많은 사건, 행동들의 조합으로 이루어지는 것 처럼 말이야.

  • 함수를 실행하는 것을 보통 호출(call) 한다고 표현해. (예: "햄버거_만들기 함수를 호출(call)해서 햄버거 를 받아온다.")
  • 함수 내에서 자기자신의 함수를 또 호출할 수도 있어. 이를 재귀함수(Recursive Function) 라고 해. 재귀함수는 반복문과 비슷해서, 탈출조건이 없으면 무한히 함수를 호출하기 때문에 반드시 특정 조건에서 return 하여 빠져나올 수 있도록 구현해야 해.

Python 으로 햄버거 값 계산하는 함수 만들기

나를 포함하여 총 5명의 친구들과 햄버거 가게에 방문했다고 생각해보자. 치즈버거는 3,000원, 불고기버거는 3,500원 이었는데, 치즈버거 2개와 불고기버거 3개를 구매하려고 해.
이 가격을 구하려는 경우 파이썬으로 아래와 같은 로직으로 구현할 수 있어.

hamburger_price = calc_hamburger('치즈', 2)
hamburger_price += calc_hamburger('불고기', 3)

def calc_hamburger(product, amount):
    price = 0  # 변수 초기화
    if (product is '치즈'):
        price = 3000
    elif (product is '불고기'):
        price = 3500
    else:
        raise "올바르지 않은 상품명"
    
    return price * amount

이렇게 로직을 구현할 수도 있고, 파이썬의 dictionary 기능을 이용하면 아래와 같이 좀 더 간단히 구현할 수도 있어.

def calc_hamburger(product, amount):
    price_dict = {  # 변수 초기화
        "치즈": 3000,
        "불고기": 3500,
    }
    return price_dict[product] * amount

여기서 5명이 정확히 n분의 1을 하기로 했다고 치자, 그럼 아래의 코드를 추가할 수 있겠지.

result = divide(hamburger_price, 5)
print(f"최종 지불할 금액은 {result}원 입니다.")

def divide(num, n):
    return num / n

이렇게 2개의 간단한 함수를 만들어봤어. 함수는 이처럼 한 번 만들어 놓으면 평생 해당 기능을 사용할 수 있지. 함수에 넣는 파라미터(Parameter) 값만 바꾸면서 원하는 대로 계속해서 사용 가능해.

Python 전체 코드

hamburger_price = calc_hamburger('치즈', 2)
hamburger_price += calc_hamburger('불고기', 3)
result = divide(hamburger_price, 5)
print(f"최종 지불할 금액은 {result}원 입니다.")

def calc_hamburger(product, amount):
    price_dict = {  # 변수 초기화
        "치즈": 3000,
        "불고기": 3500,
    }
    return price_dict[product] * amount

def divide(num, n):
    return num / n

JavaScript 의 함수

JavaScript 에서 함수를 정의(definition) 하는 방법을 알아보자.

function 함수이름(변수1, 변수2, ... 변수n) {  // 함수의 정의
    ...  // 로직(logic, 논리, 알고리즘)
    return 결과값;
}

const 결과값 = 함수이름(변수1, 변수2, ... 변수n);  // 정의된 함수를 실행하여 결과값을 받음

자바스크립트에서 함수를 정의할때는 function 이라는 키워드를 사용해.
function 키워드를 사용하지 않고도 함수를 정의할 수 있는데, 그 방법은 아래와 같아.

const 함수이름 = (변수1, 변수2, ... 변수n) => {  // 함수의 정의
    ...  // 로직(logic, 논리, 알고리즘)
    return 결과값;
};

const 결과값 = 함수이름(변수1, 변수2, ... 변수n);  // 정의된 함수를 실행하여 결과값을 받음

위의 형태로 작성하는 함수를 Arrow function(화살표 함수) 이라고 이야기 해.
=> 이 표시를 Fat arrow,
-> 이 표시를 Thin arrow 라고 하는데,
그래서 이런 형태의 함수를 Arrow function 이라고 불러.

일반 함수와 화살표 함수 둘 다 알아야 하느냐고? 응 맞아. 둘 다 매우 자주 사용되는 형태야.
일반 function 과 Arrow function 은 몇가지 차이가 있지만, 지금처럼 입문단계에서 그런 세부적인 것까지 알 필요는 없어. 지금은 일반함수만 사용할거고, 가장 중요한 함수(function)의 본질적인 사용법에 대해서만 알아가면 돼.

자바스크립트는 파이썬과 함수의 용법이 크게 다르지 않아. 하지만 그렇다고 해서 자바스크립트 코드를 작성할 때, 파이썬의 코드를 복사-붙여넣기 하는 방식은 정말 최악이야. 그러면 아무런 학습이 되지 않아.

'백문이불여일타' 기억하지? 이 책을 공부하면서 절대 복사-붙여넣기 기능을 사용하지 말자. 하나하나 직접 타이핑 해야 머릿속에 남고 손에도 익을거야.

JavaScript 전체코드

let hamburgerPrice = calcHamburger('치즈', 2);
hamburgerPrice += calcHamburger('불고기', 3);
const result = divide(hamburgerPrice, 5);
console.log(`최종 지불할 금액은 ${result}원 입니다.`);

function calcHamburger(product, amount) {
    const priceDict = {  // 변수 초기화
        치즈: 3000,
        불고기: 3500,
    };
    return priceDict[product] * amount;
}

function divide(num, n) {
    return num / n;
}

Java 의 함수

자바는 파이썬, 자바스크립트와는 다르게 Data-type(자료형) 에 대해서 매우 엄격하기 때문에 이 점을 항상 신경써줘야 해.

자바의 함수 선언 방법은 아래와 같아.

반환자료형 함수이름(자료형 변수1, 자료형 변수2, ... 자료형 변수n) {  // 함수의 정의
    ...  // 로직(logic, 논리, 알고리즘)
    return 결과값;
}

자료형 결과값 = 함수이름(변수1, 변수2, ... 변수n);  // 정의된 함수를 실행하여 결과값을 받음

자바는 변수를 선언할 때나, 함수를 정의할 때나 모두 명확한 자료형을 먼저 입력해야만 해.

Java 전체코드

int hamburgerPrice = calcHamburger('치즈', 2);
hamburgerPrice += calcHamburger('불고기', 3);
int result = divide(hamburgerPrice, 5);
System.out.println("최종 지불할 금액은 " + result + "원 입니다.");

int calcHamburger(String product, int amount) {
    int price = 0;  // 변수 초기화
    if (product == '치즈') {
        price = 3000;
    } else if (product == '불고기') {
        price = 3500;
    } else {
        throw "올바르지 않은 상품명";
    }
    return price * amount;
}

int divide(int num, int n) {
    return num / n;
}

함수에 대한 정리

함수는 프로그래밍에서 하나의 '행동' 을 추상화(Abstract)하여 정의한다고 말할 수 있어.
나중에 Object(객체)에 대해서 설명할 때에 상태값과 행동에 대해서 많은 이야기들을 할 예정이지만, 그 전에 여기에서도 이야기 해주고 싶어.

프로그래밍은 결국 '상태값' 과 '행동' 의 조합이라고 생각하면 돼.
프로그래밍에서 상태값은 변수이고, 행동은 함수야.
마찬가지로 위에서 선언한 함수들도 모두 행동이지. "햄버거 만들기", "햄버거값 계산하기", "값 나누기" 라는 행동.
그리고 상태값은 "지불해야할 햄버거 가격" 이었어.

이 개념을 머리에 넣고 상상하면서 코딩하면 더욱 재미있을거고, 훨씬 쉽게 문제를 풀어나갈 수 있을거야!

연습 문제

다음 과정을 시작하기 전에 반드시 풀고 가야할 문제에요. 이 문제를 해결하지 못하는 상태에서 다음 과정으로 넘어가면 안돼요. 어렵지 않으니까 천천히 읽고 구현해보세요.

  1. 이전에 만들었던 자료형 & 변수 (Data type & Variable) 영역의 연습문제 코드들을 introduce_myself() 라는 함수로 선언하고 실행하세요.
  2. 연산자 (Operator) 영역의 연습문제 코드들을 match_year() 라는 함수로 선언하고 실행하세요.
  3. 조건문 (Conditional Statements) 영역의 연습문제 코드들을 charge_phone() 라는 함수로 선언하고 실행하세요.
  4. 반복문 (Loop statement) 영역의 연습문제 코드들을 loop_stars() 라는 함수로 선언하고 실행하세요.
  5. 각각의 함수들이 무슨 역할을 하는지 한 문장으로 추상화시켜서 함수 선언부 위에 주석으로 작성해보세요.
  6. 기존 코드를 함수로 감싼것 뿐이기 때문에, 실행 결과는 기존의 코드에 대한 실행결과와 100% 동일해야 해요.

만약 실행 후 아무런 결과가 출력되지 않는다면?
함수를 선언만 해놓고, 실행시키지 않는 것은 자주 하는 실수 중 하나에요. 만약 변수를 선언해놓고 아무데도 사용하지 않는다면 아무런 의미가 없겠죠? 함수도 마찬가지에요. 선언만 해놓고 실행하는 코드를 작성하지 않는다면 아무런 효과가 없답니다.

  • 지금 Python 과정을 하고 있다면, Python 으로 풀어보세요.
  • Python 과정을 모두 마치고 JavaScript 를 시작했다면, JavaScript 로 풀어보세요.
  • JavaScript 과정을 모두 마치고 Java 를 시작했다면, Java 로 풀어보세요.
  • 너무 어려우면 ChatGPT 에게 도움을 요청해보세요.
profile
🔥 from Abstraction to Realization

0개의 댓글