欢迎关注github项目,100天Mojo从新手到大师
函数
和其他语言一样,要写出高质量的代码首先要解决的就是重复代码的问题,可以将重复的代码逻辑封装到一个称之为“函数”的功能模块中,在需要使用的地方只需要“调用”这个“函数”就可以了。
函数定义
在Mojo中支持两种类型的函数:def 函数和 fn 函数。可以将任一种声明方式与任何函数(包括 main() 函数)一起使用,但它们具有不同的默认行为。两种方式都有好的用例,没有某一个比另一个好,决定使用哪种方式是个人喜好的问题,或者哪种风格最适合给定的任务。
def语句
def 函数与Python def 函数具有相同的动态性和灵活性。例如,此函数在 Python 和 Mojo 中的工作原理相同:
def greet(name):
greeting = "Hello, " + name + "!"
return greeting
此外,在Mojo中,def 语句还可以选择指定参数类型、返回类型和变量可变性,如下所示:
def greet(name: String) -> String:
let greeting = "Hello, " + name + "!"
return greeting
这样,编译器可以确保 name 是字符串,返回类型是字符串,并且 greeting 变量不可变。
fn语句
fn 函数提供严格的类型检查和额外的内存安全性。例如,这是使用 fn 实现上面的相同函数:
fn greet(name: String) -> String:
let greeting = "Hello, " + name + "!"
return greeting
就函数调用者而言, 两种方式定义的函数是可以互换的。也就是说,没有什么是 def 能做 fn 不能的(反之亦然)。不同之处在于,与 def 相比, fn 内部更严格,它要求函数参数的类型和返回值的类型必须匹配。
通过强制执行类型检查,使用 fn 定义函数有助于避免各种运行时错误。与 def 函数中的动态类型相比,它还提高了性能,因为不需要开销处理来确定在运行时使用哪些数据类型 - 类型在编译时是固定的。
参数
可选参数
可选参数是包含默认值的参数,例如此处的 exp 参数:
fn pow(base: Int, exp: Int = 2) -> Int:
return base ** exp
fn use_defaults():
# Uses the default value for `exp`
let z = pow(3)
print(z)
关键字参数
还可以使用参数名称指定参数值:
fn pow(base: Int, exp: Int = 2) -> Int:
return base ** exp
fn use_keywords():
# Uses keyword argument names (with order reversed)
let z = pow(exp=3, base=2)
print(z)
Mojo 目前仅包含对关键字参数的部分支持,因此尚不支持某些功能,例如强制关键字参数和可变关键字参数(类似Python中的 **kwargs)
可变参数
如果希望函数接收可变引用,需要在参数名称前面添加 inout 关键字,这意味着对函数内部值的任何更改在函数外部都是可见的。
例如,这个 mutate() 函数会更新原始 x 值:
def mutate(inout y: Int):
y += 1
var x = 1
mutate(x)
print(x)
这等效于下面的代码:
def mutate_copy(y: Int) -> Int:
y += 1
return y
var x = 1
x = mutate_copy(x)
print(x)
但它的内存效率更高,因为它不会复制该值。
但是,需要注意的是,传递为 inout 的值必须已经是可变的。例如,如果使用如下方式定义的变量 let x ,则会出现编译器错误,因为 Mojo 无法形成对不可变值的可变引用( let 使变量不可变)。
重载函数
如果 def 函数未指定参数类型,则它可以接受任何数据类型,并决定如何在内部处理每种类型。因此通常不需要为 def 函数编写重载。
另一方面,所有 fn 函数都必须指定参数类型,因此,如果希望函数使用不同的数据类型,则需要实现函数的单独版本,每个版本指定不同的参数类型。这称为“重载”函数。
例如,下面是一个重载 add() 函数,可以接受 Int 或 String 类型:
fn add(x: Int, y: Int) -> Int:
return x + y
fn add(x: String, y: String) -> String:
return x + y
如果将 Int String以外的任何内容传递给 add() 函数,则会出现编译器错误。
重载也适用于两者 fn 和 def 函数的组合。例如,可以定义多个 fn 函数重载,然后定义一个或多个未指定所有参数类型的 def 版本作为应变方案。