Mojo의 function system
1. function define(def vs. fn)
1.1 def
먼저 모조는 기본적으로 파이썬과의 호환성을 챙기고 있으므로 파이썬에서 사용하는 방식을 그대로 사용할 수 있습니다
그래서 def
keyword를 이용해서 함수를 정의할 수 있습니다
def sum(a, b):
return a+b
이렇게 정의할 수 있습니다. 물론 명시적으로 타입을 적어서 더 안전하게 처리할 수도 있죠
def sum1(a: Int32, b: Int32) -> Int32:
return a + b
def sum2(a: Int32, b: Int32):
return a + b
안되는 모양
def sum3(a, b) -> Int32:
return a+b
error: cannot implicitly convert ‘object’ value to ‘SIMD[int32, 1]’ return a+b
이거를 보면 알 수 있듯이 return datatype이 정해져 있어서 object로 들어오는 a, b의 type을 변환하는데 이때 모조가 암시적 타입 변환(implicit type conversion)을 못해서 터지는 오류입니다
뭐 어떻게든지 선언할 수 있어요
그래서 이걸 좀 정리를 해보자면
- 인자 타입을 명시하지 않아도 됨
- 리턴 타입을 명시하지 않아도 됨
- 타입을 명시하지 않은 인자는
object
타입으로 처리 - 인자는 가변적(mutable)
- 객체 타입 인자는 참조로 전달
- 다른 선언된 타입의 인자는 값으로 전달
이렇게 됩니다!
1.2 fn
fn
같은 경우에는 좀 많이 엄격합니다. def
의 경우 선택적이었던 타입 명시를 강제하고 있거든요!
fn sum1(a: Int32, b: Int32) -> Int32:
return a + b
이렇게만! 사용할 수 있어요
바로 또 정리를 해봅시다.
- 모든 인자의 타입을 명시해야 함
- 리턴 타입을 명시해야 함 (값을 반환하지 않는 경우 제외)
- 기본적으로 인자는 불변 참조로 전달
- 오류를 발생시키는 경우 raises 키워드를 명시해야 함
이렇게 정리할 수 있습니다.
2. 함수의 활용
지금부터는 fn
키워드를 주로 사용하겠습니다.
2.1 함수 인자
2.1.1 선택적 인자
fn pow(base: Int, exp: Int = 2) -> Int:
return base ** exp
이렇게 기본값을 지정할 수 있음.
2.1.2 키워드 인자
var value_case_1 = pow(base=3, exp=5)
var value_case_2 = pow(exp=3, base=5)
호출을 이렇게 활용할 수도 있습니다.
2.1.3 가변 인자
fn sum(*values: Int) -> Int:
var sum: Int = 0
for value in values:
sum = sum + value
return sum
fn main():
var value_case_1 = sum(10)
print(sum(1, 2, 3)) # -> 6
print(sum(10, 20, 30, 40)) # -> 100
print(value_case_1) # -> 10
return
이렇게 활용할 수 있어요!
2.1.4 키워드 전용 인자
fn kw_only_args(a1: Int, a2: Int, *, double: Bool) -> Int:
var product = a1 * a2
if double:
return product * 2
else:
return product
fn kw_only_args2(a1: Int, a2: Int, *, multi: Bool, amount: Int) -> Int:
var product = a1 * a2
if not multi: return product
return product ** (amount+1)
fn main():
print(kw_only_args(1, 2, double=True))
print(kw_only_args2(1, 2, multi=True, amount = 3))
return
여기서 보면 중간에 *
가 중요한데 이 기호 뒤에오는 모든 매개변수가 키워드 전용 인자라고 말하는 겁니다
이때 키워드를 직접 명시하지 않는다면?
print(kw_only_args2(1, 2, True, amount = 3))
이렇게되면 오류가 터지게 됩니다.
2.1.5 함수 오버로딩
fn add(x: Int, y: Int) -> Int:
return x + y
fn add(x: String, y: String) -> String:
return x + y
오버로딩나온 김에 정리하고 갑시다.(원래 저는 자바를 먼저 사용했기에..)
같은 이름의 메서드를 여러 개 정의할 수 있게 해주는 기능인데,
// 정수 두 개를 더하는 메서드 public int add(int a, int b) { return a + b; } // 실수 두 개를 더하는 메서드 public double add(double a, double b) { return a + b; } // 정수 세 개를 더하는 메서드 public int add(int a, int b, int c) { return a + b + c; }
자바에서는 이런 식으로 예시를 들 수 있습니다.
조건은
- 메서드 이름이 같아야 함
- 매개변수의 개수나 타입이 달라야 함
- 반환 타입은 오버로딩을 구분하는 기준이 되지 않음!
이렇게 되겠습니다.
fn add(x: Int, y: Int) -> Int: return x + y fn add(x: Int, y: Int, z: Int) -> Int: return x + y + z fn add(x: Float32, y: Float32) -> Float32: return x + y fn add(x: String, y: String, delimiter: String) -> String: return x + delimiter + y fn main(): print(add(5, 3)) # 두 정수의 합: 8 print(add(1, 2, 3)) # 세 정수의 합: 6 print(add(3.14, 2.86)) # 두 실수의 합: 6.0 print(add("Hello", "World", " ")) # 구분자를 포함한 문자열 연결: "Hello World"
이런식으로 사용할 수 있겠습니다!
2.2 오류 처리
2.2.1 오류를 발생시키는 함수
def raises_error():
raise Error("This is the error")
fn handle_error():
try:
raises_error()
except e:
print("Handled an error," e)
fn
함수에서 오류를 발생시키려면 raises
키워드를 사용해야 합니다. raises
키워드가 없는 fn
함수는 오류를 전파할 수 없습니다.
def divide(a: Int, b: Int) -> Float64:
if b == 0:
raise Error("0으로 나눌 수 없습니다!")
return a / b
fn safe_divide(a: Int, b: Int) -> Float64:
try:
return divide(a, b)
except e:
print("나눗셈 오류 발생:", e)
return 0.0
fn main():
print(safe_divide(10, 0)) # 나눗셈 오류 발생: 0으로 나눌 수 없습니다! \n 0.0
print(safe_divide(10, 1)) # 10.0
이렇게 활용할 수 있겠네요!
포스트가 길어지고, 추가로 다뤄야할 내용이 있어서 파트를 나눠서 하도록 하겠습니다!