-
함수 정의하기컴공지식/프로그래밍언어론 2024. 9. 28. 19:56
함수를 정의하기 위해선 구체적인(concrete) 문법과 추상적인(abstract) 문법을 정의해야 한다.
구체적인 문법은 프로그래밍 언어에서 실제로 작성되는 코드 형태이고 추상적인 문법은 코드를 프로그램 내부에서 어떻게 해석하고 처리하는지에 대한 구조다.
우리는 지금까지 단순한 연산 과정인 AE를 거쳐 with를 쓰는 WAE까지 알아봤다.
이제 함수가 추가된 형태인 F1WAE를 알아볼 차례이다.
이 F1WAE에서는 함수를 다음과 같이 정의할 수 있다.
{deffun {twice x} {+ x x}}
twice라는 함수를 만든건고, 옆의 x는 파라미터가 된다.
그 옆의 괄호는 x와 x를 더하는거다. 그러니 만약 {twice 2}라는 함수가 있다면 4가 반환되는거다.
아무튼 함수 정의는 위처럼
{deffun {id id} Expr}
이런식으로 정의된다.
첫 번째 id는 함수 이름이고, 두 번째 id는 인자(매개변수)를 의미한다.
Expr은 함수에서 수행하는 본문을 나타낸다.
함수 호출은 {id Expr} 형태로 이루어진다.
id는 호출할 함수 이름이고, Expr은 전달될 인자를 의미한다.
이제 F1WAE에서 함수를 정의하는 추상 문법에 대해 알아보자
case class FunDef(funName: Symbol, argName: Symbol, body: Expr)
funName은 함수 이름, argName은 매개변수, body는 함수의 본문을 의미한다.
이 클래스는 Expr을 확장하지 않는데 함수 자체는 표현식으로 취급되지 않는다는 의미다.
이 함수 호출을 이용하기 위해 추상문법을 또 만들어보자면..
App(ftn: Symbol, arg: Expr)
이렇게 만들 수 있다.
ftn은 함수 이름, arg에는 인자값이 된다.
만약 {deffun {identity x} x}라는 구체적인 문법을 사용하여 함수를 정의했을 경우, 컴퓨터는 이를
FunDef(Id(identity), Id(x), Id(x)) 다음과 같이 해석한다.
그리고 {identity 8}로 함수를 사용하려고 하면
함수를 호출하는 추상문법을 사용한다.
App(Id(identity), Num(8))
{deffun {twice x} {+ x x}}라고 정의했으면
FunDef(Id(twice), Id(x), Add(Id(x), Id(x))) 이로 해석하고
{twice 10}으로 함수를 이용하면
App(Id(twice), Num(10))
이렇게 컴퓨터가 해석해서 이용된다.
아무튼 구체적인 문법을 파서(Parser)가 추상 문법 트리로 변환시킨 후 인터프리터가 그것을 계산하여 코드의 결과가 나오게 된다.
근데 F1WAE 인터프리터에서 함수 호출(App)을 어떻게 처리할까
case App(ftn, arg) =>
val FunDef(_, argName, body) = lookupFunDef(ftn, funDefs)
interp(subst(body, argName, interp(arg, funDefs)), funDefs)
이는 인터프리터가 함수 호출 시 함수 정의를 찾아서 해당 함수의 본문을 실행하는 방식을 보여준다.
lookupFunDef(ftn, funDefs)는 함수 정의 목록(funDefs)에서 함수 이름(ftn)에 해당하는 정의를 찾아주는 함수다.
이 함수는 함수 이름(funName), 매개변수(argName), 함수 본문(body)을 포함한 FunDef 구조체를 반환한다.
함수 호출 시 전달된 인자(arg)는 매개변수(argName)로 대체돼야하는데 이 작업은 subst 함수를 사용해 이루어진다.
interp(subst(body, argName, interp(arg, funDefs)), funDefs)에서 먼저 전달된 인자(arg)를 해석한 후, 그 값을 함수 본문(body)의 매개변수(argName)로 대체한 다음, 대체된 본문을 다시 해석한다.
위에 나온 lookupFunDef 함수를 살펴보자
def lookupFunDef(name: Id, funDefs: List[FunDef]): FunDef = {
funDefs match {
case Nil => throw new SimpleException(s"Unknown function: $name")
case head :: tail =>
if (head.funName == name) head
else lookupFunDef(name, tail)
}
}name: Id: 찾고자 하는 함수의 이름
funDefs: List[FunDef]: 함수 정의들이 담긴 리스트
동작방식은
함수 정의 리스트(funDefs)를 하나씩 순차적으로 검사하면서, 함수 이름(funName)이 주어진 이름(name)과 일치하는지 확인, 만약 일치하는 함수 정의가 없다면 에러를 던진다.
case Nil 부분은 찾으려는 함수가 없을 때 에러 던지는 부분
case head :: tail => if (head.funName == name) head else lookupFunDef(name, tail) 이 부분은
리스트의 첫 번째 요소(head)를 검사하고, 함수 이름이 일치하면 해당 함수 정의를 반환한다.
일치하지 않으면 리스트의 나머지 부분(tail)에서 계속 찾는다.
'컴공지식 > 프로그래밍언어론' 카테고리의 다른 글
FWAE의 평가(Evaluation) 과정 이해하기: 일급 함수와 오류 처리 (1) 2024.10.05 Substitution의 연기 (1) 2024.10.05 Substitution(치환)에 관하여 (1) 2024.09.22 Scala로 WAE 정의하기 (0) 2024.09.11 BNF로 식별자 정의하기 (0) 2024.09.11