함수 정의하고 호출하기
R에서 함수는 숫자나 문자열과 마찬가지로 값입니다. function 키워드로 만들고 <-로 이름에 붙이는데, 다른 것을 변수에 저장하는 것과 똑같습니다.
구성 요소는 이렇습니다. function(x)는 x라는 매개변수 하나를 선언하고, 중괄호가 본문을 담으며, square(4)를 호출하면 x가 4인 상태로 본문이 실행됩니다. 본문이 한 줄이면 중괄호는 선택 사항이라 square <- function(x) x^2도 같은 함수이지만, 본문이 길어지면 중괄호가 더 잘 읽힙니다.
함수도 값일 뿐이므로 변수에 대해 아는 모든 것이 적용됩니다. 재할당하거나, 다른 함수에 전달하거나, 리스트에 담을 수 있습니다. 이 한 가지 사실이 apply 계열 전체를 포함해 관용적인 R의 절반을 떠받칩니다.
반환값: 마지막 표현식이 이깁니다
R 함수는 마지막으로 평가된 표현식이 만들어 내는 값을 반환합니다. 필수적인 return 문은 없습니다.
return()도 존재하며 기대한 대로 동작합니다. 함수를 즉시 멈추고 인자를 돌려주죠. 관용적인 R은 이를 조기 종료에 씁니다. 입력 때문에 나머지 작업이 무의미해질 때 앞부분에서 빠져나오는 용도입니다. if-else 검사와 짝지어 보세요.
마지막 줄에 return(a / b)를 쓴다고 틀린 것은 아니지만 소음일 뿐입니다. return()은 일찍 일어나는 종료에 남겨 두고, 정상 경로는 마지막 표현식이 말하게 하세요.
기본값과 이름 있는 인자
매개변수는 기본값을 가질 수 있으며, 매개변수 목록에서 =로 선언합니다. 기본값에 만족하는 호출자는 그냥 인자를 생략하면 됩니다.
세 번째 호출은 이야기의 나머지 절반인 이름 있는 인자를 보여 줍니다. 호출 지점에서 name = "Ada"라고 쓰면 위치는 더 이상 중요하지 않습니다. R은 먼저 이름으로 맞추고 남은 매개변수를 위치로 채웁니다. 여기서 실용적인 규칙 두 가지가 나옵니다.
- 필수 매개변수를 앞에, 기본값이 있는 것을 뒤에 두어 위치 기반 호출이 자연스럽게 유지되도록 하세요.
- 호출 지점에서 위치만으로는 의미가 분명하지 않은 인자에는 이름을 붙이세요.
greet("Ada", punctuation = "?")가 독자에게 쉼표를 세게 만드는 것보다 명확합니다.
...으로 인자 넘겨주기
특수 매개변수 ...("점들"이라 읽습니다)는 "몇 개든 추가 인자"를 뜻합니다. ...를 선언한 함수는 목록에 없던 인자를 받아 다른 함수로 전달할 수 있습니다.
shout()은 단어를 몇 개 넘기든 알지도, 신경 쓰지도 않습니다. 전부 paste()에 넘겨 줄 뿐이죠. punctuation이 점들 뒤에 온다는 점에 주목하세요. ... 뒤에 선언된 매개변수는 이름으로만 설정할 수 있는데, 옵션에는 정확히 그것이 바람직합니다. 이 전달 패턴은 기본 R 전반에 퍼져 있습니다. sapply(x, round, digits = 2)가 동작하는 이유는 sapply가 자신의 ...를 통해 digits = 2를 round로 전달하기 때문입니다.
점들의 대가는 오타가 조용히 지나간다는 것입니다. punctuation을 punctation으로 잘못 쓰면 오류가 나는 대신 그냥 ...로 삼켜집니다. 점들을 쓰는 함수는 작게 유지하세요.
익명 함수
함수가 한 번만 필요할 때, 보통 다른 함수의 인자로 쓸 때는 이름을 아예 생략할 수 있습니다.
두 줄은 같은 일을 합니다. function(x) x^2가 고전적인 표기이고, \(x) x^2는 R 4.1에서 추가된 축약형으로 \가 function이라는 단어를 대신합니다. 타이핑 수를 제외하면 모든 면에서 동일하며, 축약형이 다른 종류의 함수인 것은 아닙니다.
연산이 한 호출 지점에 묶인 한 줄짜리일 때 익명 함수를 쓰세요. 같은 람다를 두 곳에 붙여 넣게 되거나 한 줄을 넘어가는 순간 이름을 주세요. 이름 있는 함수는 시험할 수 있고 스스로를 설명합니다.
스코프: 함수가 볼 수 있는 것
R은 렉시컬 스코프를 사용합니다. 함수가 자신의 매개변수와 지역 대입에서 찾을 수 없는 변수를 필요로 할 때, 호출된 곳이 아니라 함수가 정의된 환경을 들여다봅니다. 실제로는 함수가 주변 스크립트의 변수를 읽을 수 있다는 뜻입니다.
반대편도 있습니다. 함수 안의 대입은 결코 밖으로 새어 나가지 않습니다. change_inside 안의 rate <- 0.5는 함수가 반환될 때 사라지는 새로운 지역 변수를 만들고, 바깥의 rate는 여전히 0.1을 출력합니다. 이는 기능입니다. 함수가 여러분의 작업 공간을 실수로 짓밟을 수 없으니까요. 다만 안쪽 대입이 유지되기를 기대한 입문자에게는 놀랍게 느껴집니다. 함수가 결과를 바깥세상에 건네야 한다면 반환하고 호출 지점에서 대입하세요. (바깥으로 손을 뻗는 <<- 연산자가 있지만, 정말로 필요해지기 전까지는 코드 냄새로 여기세요.)
종합 예제
이것이 보상 패턴입니다. 분석 세 줄을 반복하고 있음을 알아차리면 함수로 감싸고 재사용하세요. 측정값 벡터를 계속 요약하고 있다고 해 봅시다.
이 페이지의 모든 것이 여기 담겨 있습니다. 기본값이 있는 digits 매개변수, 그것을 덮어쓰는 이름 있는 호출, 그리고 반환을 담당하는 마지막 표현식(round(out, digits))입니다. 이 함수는 문장처럼 읽히고, 어떤 수치 벡터에도 동작하며, 나중에 중앙값을 추가하기로 하면 한 곳만 고치면 됩니다.
핵심 정리
name <- function(args) { body }가 함수를 만들고, 마지막으로 평가된 표현식이 반환값입니다.return()은 조기 종료용이지 정상적인 마지막 줄을 위한 것이 아닙니다.- 기본값은 매개변수 목록에서
=로 선언하고, 이름 있는 인자는 호출 지점을 순서에 무관하고 읽기 쉽게 만듭니다. ...는 추가 인자를 받아 전달합니다. 강력하지만 오타를 숨깁니다.function(x)와\(x)는 같은 것입니다. 일회성 한 줄짜리에 익명 함수를 쓰세요.- 함수는 정의된 곳의 변수를 읽고, 함수 안의 대입은 안에 머무릅니다.
다음 순서는 apply 계열입니다. 여러분의 함수를 받아 벡터와 리스트 전체에 실행해 주는 함수들입니다.
자주 묻는 질문
R에서 함수는 어떻게 만드나요?
function 객체를 이름에 대입하세요. square <- function(x) { x^2 }처럼 씁니다. 매개변수는 괄호에, 본문은 중괄호에 넣고 square(4)처럼 호출합니다. 마지막으로 평가된 표현식이 함수의 반환값입니다.
R에서 return()이 꼭 필요한가요?
아니요. R 함수는 마지막으로 평가한 표현식의 값을 자동으로 반환합니다. return()도 존재하지만, 관용적인 R에서는 조기 종료용으로 남겨 둡니다. 입력이 유효하지 않거나 예외 상황을 처리할 때 함수 앞부분에서 빠져나오는 용도입니다.
R에서 익명 함수란 무엇인가요?
이름을 붙이지 않고 정의하는 함수로, 보통 다른 함수에 곧바로 전달합니다. 예: sapply(x, function(v) v^2). R 4.1부터는 축약형 \(v) v^2도 있으며, 타이핑만 줄었을 뿐 정확히 같은 의미입니다.