Mojo —— 版本更新 v24.4

Mojo —— 版本更新 v24.4

前言

Mojo v24.42024-06-07 更新,主要更新了

  • 改进了 def 的性能并更容易使用
  • 继续围绕 UnsafePointer 类型统一标准库 API
  • 改进了许多集合类型
  • 改进了 Dict 的插入性能
  • 新的 @parameter for 替代了早期(不太可靠的) @unroll 装饰器

下面我们介绍一下具体做了哪些更新

语言层面

函数

def 函数更新

之前的版本中,def 默认使用 owned 方式传递参数,这会导致不必要的拷贝并造成性能损耗。

所以在这个版本中,def 的默认行为变为 borrowed(借用),但是,只有当值是可变的时候会对值进行局部拷贝。

现在,你可以将 def 函数的返回值设置为元组类型。例如

def return_two(i: Int) -> (Int, Int):
  return i, i+1

a, b = return_two(5)
print(a, b)
# 5 6

同时,隐式的变量声明可以遮蔽全局不可变标识符(如模块名字或内建函数名)。例如

slice = foo()
自动解引用

Mojo 函数可以在结果类型说明符中使用一个新的 ref 关键字返回对存储的自动解引用。例如:

@value
struct Pair:
    var first: Int
    var second: Int

    fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int:
        return self.first

fn show_mutation():
    var somePair = Pair(5, 6)
    print(somePair.get_first_ref())
    somePair.get_first_ref() = 1
    print(somePair.get_first_ref())
# 5
# 1

这种方法提供了一种返回给定类型的 自动解引用 的通用方法。这一功能与 __refitem__ 重叠,所以 __refitem__ 已被删除,取而代之的是返回引用的 __getitem__

只推断参数

Mojo 增加了对只推断编译时参数的支持,即只能通过传入值来推断其类型,而不能手动指定。

只推断编译时参数必须出现在参数列表的开头,用户不能手动指定。它们被声明在 // 标记的左侧,就像位置参数一样。

定义这种带有依赖参数的函数,在调用时无需调用者指定所有必须参数。例如

fn parameter_simd[dt: DType, //, value: Scalar[dt]]():
    print(value)

fn call_it():
    parameter_simd[Int32(42)]()

也可以用在结构体中。例如

struct ScalarContainer[dt: DType, //, value: Scalar[dt]]:
    pass

fn foo(x: ScalarContainer[Int32(0)]):
    pass
编译时参数的重载

现在,可以对编译时参数进行重载了。例如

fn overloaded_parameters[value: Int32]():
    pass

fn overloaded_parameters[value: Float32]():
    pass

fn form_reference():
    alias ref = overloaded_parameters[Int32()]
deprecated 装饰器

Mojo 现在支持在结构体、函数、特性、别名和全局变量上添加 @deprecated 装饰器。

该装饰器将所附声明标记为已废弃,在用户代码中引用已废弃声明时会发出警告。

@deprecated("Foo is deprecated, use Bar instead")
struct Foo:
    pass

fn outdated_api(x: Foo): # warning: Foo is deprecated, use Bar instead
    pass

@deprecated("use another function!")
fn bar():
    pass

fn techdebt():
    bar() # warning: use another function!
parameter for 参数

现在可以对 for 循环使用 @parameter,用于替代原先的 unroll 装饰器,其中循环序列中使用的值必须是编译时参数值。例如

fn parameter_for[max: Int]():
    @parameter
    for i in range(max)
        @parameter
        if i == 10:
            print("found 10!")

目前,@parameter for 要求序列的 __iter__ 方法返回 _StridedRangeIterator 类型的值,将来可能会取消这一限制。

其他

ReferenceAnyLifetimeis_mutable 编译时参数现在是 Bool 类型,而不是低级的 __mlir_type.i1 值。

现在,Mojo 将根据搜索路径(PATH)上的 Python 链接到一个 Python 动态库。这样,您就可以激活一个虚拟环境(如 conda),并访问安装在该环境中的 Python 模块,而无需设置 MOJO_PYTHON_LIBRARY

AnyRegType 已更名为 AnyTrivialRegTypeMojo 现在禁止将非 trivial 型寄存器可传递类型绑定到 AnyTrivialRegType。这弥补了语言中的一个主要安全漏洞。今后,通用代码请使用 AnyType

完全删除 let 关键字。

标准库层面

新的特性

内建的函数 repr 用于 Representable 特性

添加 Indexer 特性来表示实现了 __index__ 方法的类型,可以在常见的 __getitem____setitem__ 中使用,并可以对它们使用内置的 index 函数。

大多数标准库容器现在可以通过实现 Indexer 的任何类型进行索引。例如:

@value
struct AlwaysZero(Indexer):
    fn __index__(self) -> Int:
        return 0

struct MyList:
    var data: List[Int]

    fn __init__(inout self):
        self.data = List[Int](1, 2, 3, 4)

    fn __getitem__[T: Indexer](self, idx: T) -> Int:
        return self.data[index(idx)]

print(MyList()[AlwaysZero()])  # 1

符合 Indexer 特性的类型可隐式转换为 Int。这意味着您可以编写使用 Int 的通用 API,而不必让它们使用符合 Indexer 的通用类型。例如

@value
struct AlwaysZero(Indexer):
    fn __index__(self) -> Int:
        return 0

@value
struct Incrementer:
    fn __getitem__(self, idx: Int) -> Int:
        return idx + 1

var a = Incrementer()
print(a[AlwaysZero()])  # 1

添加了几个支持内置函数和数学函数的特性。包括

函数特性魔法方法
absAbsable__abs__
powPowable__pow__
roundRoundable__round__
math.ceilmath.Ceilable__ceil__
math.ceildivmath.CeilDivable/math.CeilDivableRaising__ceildiv__
math.floormath.Floorable__floor__
math.truncTruncable__trunc__

注意

  1. 实现 Powable 的类型可以使用 ** 操作符
  2. 对于 ceildiv,只要实现 CeilDivableCeilDivableRaising 特性之一即可
  3. CeilableCeilDivableFloorable 特性在 math 包中,Truncable 目前是内置的,不需要导入,后面会再进行调整

使用方式如下

from math import sqrt

@value
struct Complex2(Absable, Roundable):
    var re: Float64
    var im: Float64

    fn __abs__(self) -> Self:
        return Self(sqrt(self.re * self.re + self.im * self.im), 0.0)

    fn __round__(self) -> Self:
        return Self(round(self.re, 0), round(self.im, 0))

    fn __round__(self, ndigits: Int) -> Self:
        return Self(round(self.re, ndigits), round(self.im, ndigits))

benchmark

在 benchmark 包中添加了 Bencher 模块用于基准测试

String

现已删除内置类/类型隐式转换为 String 类型。需要使用 str 将类型转换为字符串。

添加了符合 Python 通用分隔符的 String.isspace 方法。它取代了字符串模块中的 isspace 函数。

String.split 现在默认使用空白符,并具有 Pythonic 行为,即默认删除所有相邻的空白。

String.striplstriprstrip 现在可以删除空白以外的自定义字符。

String 现在有了 splitlines 方法,可以在行边界分割字符串。该方法支持通用换行符,并提供了保留或删除换行符的选项。

InlinedString 已重命名为 InlineString 以与其他类型保持一致。

StringRef 现在实现了 strip,可用于移除前导和尾部空白,并实现了 startswithendswith

新增了 StringSlice 类型,以取代标准库代码中使用的不安全 StringRef 类型。StringSlice 是对编码字符串数据的非所有权引用。

StringRef 不同,StringSlice 与其指向的数据的生命周期安全地绑定在一起。

StringStringLiteral 添加了新的 as_string_slice 方法。

可以使用 UnsafePointer 和长度来初始化 StringSlice

StringStringLiteral 添加了新的 as_bytes_slice 方法,能够返回字符串所有字节的 Span 类型。

继续为 String 类型添加转换为 UnsafePointer 和无符号字节类型的支持:

  • String._as_ptr 更名为 String.unsafe_ptr,并将返回类型更改为 UnsafePointer
  • StringLiteral.data 更名为 StringLiteral.unsafe_ptr,并将返回类型更改为 UnsafePointer
  • InlineString.as_ptr 已更名为 unsafe_ptr,现在返回 UnsafePointer[UInt8]
  • StringRef.data 现在是 UnsafePointerStringRef.unsafe_ptr 现在返回 UnsafePointer[UInt8]

其他内建函数

Slice.__len__ 函数已被删除,Slice 不再符合 Sized 特性。这避免了语义上的歧义:切片的长度始终取决于被切片对象的长度。

如果需要使用现有功能可以使用 Slice.unsafe_indices 方法。该方法明确指出,该实现不会检查切片边界是否具体或在任何给定对象的长度范围内。

为符合 ComparableCollectionElement 特性的元素列表添加了内置 sort 函数。

int 现在可以使用字符串和指定的进制数将字符串解析为整数:int("ff", 16) 返回 255

此外,如果指定进制为 0,那么字符串将被当作整数字面进行解析,进制由字符串是否包含前缀 "0x""0o""0b" 决定。

添加了 bin 内置函数,用于将整数类型转换为二进制字符串表示。

添加了 atof 内置函数,它可以将字符串转换为 float64

现在,你可以使用内置的 anyall 函数来检查集合中的布尔元素。

由于 SIMD.__bool__ 现在限制为 size=1,因此必须显式地使用这些函数才能获得具有多个元素的 SIMD 向量的布尔值。这避免了 SIMDBool 之间隐式转换的常见错误。例如

fn truthy_simd():
    var vec = SIMD[DType.int32, 4](0, 1, 2, 3)
    if any(vec):
        print("any elements are truthy")
    if all(vec):
        print("all elements are truthy")
# any elements are truthy

现在 object 现在实现了所有位运算符。

Tuple 实现了 __contains__ 方法,可以进行成员判断。例如

var x = Tuple(1, 2, True)
if 1 in x:
    print("x contains 1")

ListLiteralTuple 现在只要求元素类型是可移动的。因此,ListLiteralTuple 本身不再是可复制的。

UnsafePointer

UnsafePointer[Scalar[_]] 指针添加了新的 memcpy 重载。

删除了 UnsafePointer 和其他指针类型中的 get_null 方法。请使用默认构造函数:UnsafePointer[T]()

许多返回指针类型的函数已统一为 unsafe_ptr 公共 API 函数。

集合

List 现在有了一个 index 方法,可以让你找到一个元素在 EqualityComparable 类型的 List 中的(第一个)位置。例如

var my_list = List[Int](2, 3, 5, 7, 3)
print(my_list.index(3))  # prints 1

List 现在可以用简化的语法转换为 String

var my_list = List[Int](2, 3)
print(my_list.__str__())  # prints [2, 3]

请注意,List 还不符合 Stringable 特性,因此您还不能使用 str(my_list)

List 有了调用 count 方法的简化语法:my_list.count(x)

List 现在支持 __contains__,因此现在可以使用列表和 in 操作符:

if x in my_list:

List 现在有了 unsafe_get,可以在不进行边界检查或包络负索引的情况下获取元素的引用。请注意,此方法是不安全的。请谨慎使用。

Dict 添加了 fromkeys 方法,以返回包含指定键和值的 Dict。添加了 clear 方法删除所有元素。

Dict 现在支持 itemsvalues 迭代器的 reversed 方法。

Dict 现在可以通过 my_dict.__str__ 简化为字符串。请注意,Dict 并不符合 Stringable 特性,因此 str(my_dict) 还无法实现。

Dict 现在实现了 get(key)get(key, default) 函数。

Dict 添加了一个临时的 __get_ref(key) 方法,允许你获取字典值的引用。

添加了一个新的 InlineList 类型,这是一个具有静态最大尺寸的栈分配列表。

添加了一种新的 Span 类型,用于获取连续集合的片段。

os

os 模块现在提供了使用 mkdirrmdir 添加和删除目录的功能。

添加了 os.path.getsize 函数,该函数以字节为单位给出路径所标识文件的大小。

添加了 os.path.join 函数

新增 tempfile 模块,以及 gettempdirmkdtemp 函数。

SIMD

已添加带 StaticIntTuple 掩码的 SIMD.shuffleSIMD.__bool__ 受到限制,只有当大小为 1 时才能工作。对于具有多个元素的 SIMD 向量,请使用 anyall

SIMD.reduce_orSIMD.reduce_and 方法现在是位操作,并支持整数类型。

已添加 SIMD.__repr__,以获取 SIMD 类型的详细字符串表示。

math

math.bit 模块已移至新的顶级 bit 模块。该模块中的下列函数已重命名:

  • ctlz -> countl_zero
  • cttz -> countr_zero
  • bit_length -> bit_width
  • ctpop -> pop_count
  • bswap -> byte_swap
  • bitreverse -> bit_reverse

函数 math.rot_bits_leftmath.rot_bits_right 已经移动到 bit 模块。

math 模块中的 is_power_of_2函数现在被称为 is_power_of_two,位于 bit 模块中。

absroundminmaxpowdivmod 函数已经从 math 移到了内置函数中,因此您不再需要导入这些函数。

math.tgamma 函数已更名为 math.gamma,以符合 Python 的命名方式。

以下函数的实现已从 math 模块移至新的 utils.numerics 模块:isfiniteisinfisannannextafterulp

math.gcd 现在可以处理负输入,并与 Python 的实现一样,接受一个可变的整数列表。还新增了针对 ListSpan 整数的重载。

协程

Coroutine 现在需要一个 lifetime 参数。解析器会在调用异步函数时自动设置该参数。它包含所有参数的生命周期以及参数的任何生命周期访问。这样就能确保只要 coroutine 仍在运行,异步函数捕获的参数就会保持有效。

Async 函数调用不再允许借用非 tirvial 的寄存器可传递类型。因为异步函数捕获它们的参数,但是寄存器可传递的类型没有生存期,所以 Mojo 不能正确地跟踪引用,这使得它不安全。为了弥补这个安全漏洞,Mojo 暂时禁止将非 trivial 的寄存器可传递类型绑定到异步函数中的借用参数。

其他

增加了一个 InlineArray 类型,它只适用于内存类型。与现有的 StaticTuple 类型相比,它在概念上是一个数组类型,但只适用于 AnyTrivialRegType

base64 包现在包括对 base64Base16编 码方案的编码和解码支持。

VariantOptional 中的 take 函数已重命名为u nsafe_take

Variant 中的 get 函数已被 __getitem__ 所取代。也就是说,v.get[T]() 应该被 v[T] 代替。

algorithm 模块中的各种函数现在都是内置函数。这包括 sortswappartitionswappartition 可能会在重新编写内置 sort 函数时对其进行优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

名本无名

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值