흐름제어 02: 함수
함수
함수(Functions)
- 스크립트와 같이 반복해서 활용하게 될 코드를 모아 놓은 것.
- 매번 동일하게 실행되는 부분과 달라지는 부분으로 구분해 볼 수 있다.
함수 만들기의 예
- 다음은 1에서 5까지의 합, 1에서 10까지의 합, 1에서 20까지의 합을 구하고 있다.
s=0
for (i in 1:5) {
s = s + i
}
print(s)
## [1] 15
s=0
for (i in 1:10) {
s = s + i
}
print(s)
## [1] 55
s=0
for (i in 1:20) {
s = s + i
}
print(s)
## [1] 210
- 여기서 공통되는 부분을 함수로 나타내는 방법은 다음과 같이 진행할 수 있다.
- 공통되는 부분을 확인한 후, 달라지는 부분을 변수로 치환한다.
- 전체를
{ }
로 감싼다. - 반환값을 명시한다:
return( )
- 함수의 이름과 인자를 명시한다:
= function(a, b, ...)
- 이를 따라 해보자.
달라지는 부분을 변수로 나타낸다.
s=0
for (i in 1:n) {
s = s + i
}
print(s)
전체를 { }로 감싼다.
{
s=0
for (i in 1:n) {
s = s + i
}
print(s)
}
반환값을 명시한다.
{
s=0
for (i in 1:n) {
s = s + i
}
print(s)
return(s)
}
함수의 이름과 인자를 명시한다.
sumToN = function(n) {
s=0
for (i in 1:n) {
s = s + i
}
print(s)
return(s) # 여기서 return은 생략할 수 있다.
}
- 이제 새롭게 정의된 함수
sumToN
을 활용하면 앞의 긴 스크립트는 다음과 같이 간단해 진다.
sumToN(5)
## [1] 15
## [1] 15
sumToN(10)
## [1] 55
## [1] 55
sumToN(20)
## [1] 210
## [1] 210
함수의 인자
- 인자가 여럿일 때 구분하는 방법은 1) 이름으로, 2) 위치로, 3) 부분 매칭(partial matching)이 있다. 다음의 함수를 예로 설명해보자.
# startNum부터 endNum까지 더하는 함수
sumAToB = function(startNum , endNum) {
s=0
for (i in startNum:endNum) {
s = s + i
}
print(s)
return(s)
8 }
- 이름으로 :
sumAToB(startNum=1, endNum=5)
,sumAToB(endNum=5,startNum=1)
- 위치로 :
sumAToB(1, 5)
- 부분 매칭으로 :
sumAToB(startNum = 1, end=5)
인자
- 인자는 기본값(default value)을 설정해 줄 수 있고, 기본값이 주어진 인자는 함수를 부를 때 생략할 수 있다.
sumAToB = function(startNum=1, endNum=10) {
s=0
for (i in startNum:endNum) {
s = s + i
}
print(s)
return(s)
}
- 함수는
...
(dot-dot-dot)을 활용하여 불특정 다수의 인자를 받아들일 수 있다. 이때에는 부분 매칭이 불가능하다. 보통 함mean
에서처럼 인자의 갯수가 가변적이거나 인자들을 다른 함수로 넘길 때 사용된다.
sqPlot = function(x, y, ...) {
plot(x, y^2, ...)
}
sqPlot((-3):3, (-3):3)
sqPlot((-3):3, (-3):3, col='red', pch=3)
- 만약 함수가 제대로 작동하기 위해 인자가 특별한 조건을 만족해야 하는 경우가 있다. 이때에는
if
문과stop("Message")
을 활용할 수 있다. 논리적 오류에 강건한 함수를 만드는데 도움이 된다. 다음의boxcox
함수는x
와lambda
두 인자를 받는데lambda
는 길이가 1이어야 한다. 만약 그렇지 않다면boxcox
함수는 에러를 발생시킨다.
boxcox = function(x, lambda=1)
{
if(length(lamdba)!=1) stop("the length of lambda must be one.")
if (lambda != 0) {
return((x^lambda -1)/lambda)
} else {
log(x)
}
}
boxcox(c(1,3,2))
## Error in boxcox(c(1, 3, 2)): object 'lamdba' not found
boxcox(c(1,3,2), 2)
## Error in boxcox(c(1, 3, 2), 2): object 'lamdba' not found
boxcox(c(1,3,2), c(1,2))
## Error in boxcox(c(1, 3, 2), c(1, 2)): object 'lamdba' not found
stopifnot(cond)
으로 대체할 수도 있다. 에러 메세지는 조건문이 그대로 출력된다.
boxcox = function(x, lambda=1)
{
stopifnot(length(lambda)==1)
if (lambda != 0) {
return((x^lambda -1)/lambda)
} else {
log(x)
}
}
boxcox(c(1,3,2))
## [1] 0 2 1
boxcox(c(1,3,2), 2)
## [1] 0.0 4.0 1.5
boxcox(c(1,3,2), c(1,2))
## Error in boxcox(c(1, 3, 2), c(1, 2)): length(lambda) == 1 is not TRUE
함수와 인자의 클래스
- 범용함수(Generic function): 인자의 클래스(class)에 따라 함수의 행동이 달라지는 함수이다.
methods( )
를 통해 클래스에 따른 함수를 확인할 수 있다.
options(max.print=10)
methods(print) # or methods('print')
## [1] print.acf* print.AES* print.all_vars* print.anova* ## [5] print.any_vars* print.aov* print.aovlist* print.ar* ## [9] print.Arima* print.arima0* ## [ reached getOption("max.print") -- omitted 249 entries ] ## see '?methods' for accessing help and source code
methods(summary)
## [1] summary.aov summary.aovlist* ## [3] summary.aspell* summary.check_packages_in_dir* ## [5] summary.connection summary.data.frame ## [7] summary.Date summary.default ## [9] summary.ecdf* summary.factor ## [ reached getOption("max.print") -- omitted 27 entries ] ## see '?methods' for accessing help and source code
print
라는 함수는 입력되는 인자의 클래스에 따라 서로 다른 함수를 부르는데 예를 들어 만약 인자의 클라스가factor
라면print.factor
라는 함수를 부른다. 만약 인자의 클라스가xyz
인데,print.xyz
라는 함수가 존재하지 않는다면,print.default
함수를 부른다.
그 밖의 몇 가지
- R은 기본적으로 Call by value이다. 함수의 인자는 인자의 주소가 아니라 값이 함수로 전달된다.
- 함수 내에서 사용된 변수는 함수가 끝나는 순간 사라진다.
- 함수 밖에서 할당된 변수는 참조할 수 있지만
=
또는<-
로 수정할 수 없다. 따라서 R의 함수 내 변수는 흔히 말하는 지역 변수(local variables)라고 할 수 있다. - 만약 함수 밖에서 할당된 변수에 새로운 값을 할당하려면 연산자
<<-
를 사용한다. - R의 어떤 함수에 대해 도움말을 보고 싶다면
?
또는help( )
를 사용한다. 예를 들어 함수read.table
에 대한 도움말을 보고 싶다면help(read.table)
을 콘솔에 입력한다.- 도움말의 첫 부분
Usage
를 보면 함수와 인자가 소개되고 인자의 기본값을 확인할 수 있다. 여기서numerals=c("allow.loss"", "warn.loss", "no.loss")
로 나타난 부분은 인자numeral
를"allow.loss"
또는"warn.loss"
또는"no.loss"
로 설정할 수 있음을 나타낸다.
- 도움말의 첫 부분
- 만약 함수
plot
에 대한 예시를 보고 싶다면example(plot)
을 해본다. 다른 함수에 대해서도example( )
를 해보자.
디버깅
- 만약 script의 부분이 이상행동을 보인다면 스크립트를 한 줄씩 실행하면서 디버깅을 할 수 있다.
- 하지만 어떤 함수 안에서 이상한 결과가 나온다면 함수를 해체해서 한 줄씩 실행하거나 R studio의 디버깅을 사용할 수 있다.
- R studio에는 함수 내부를 디버깅할 수 있는 도구가 마련되어 있다. 특히 break-point를 지정하는 방법을 숙지하자. 에러가 발생되었을 때, Show Traceback, Rerun with Debug 등을 사용할 수 있다.
Leave a comment