Mojo 学习 —— 内置结构与函数

Mojo 学习 —— 内置结构与函数


学习完 Mojo 基本语法之后,我们接着学习 Mojo 标准库的使用。首先 介绍一些 Mojo 中的内置结构与函数。

内置列表

内置列表包含 4 个结构体

  • ListLiteral: 异构列表字面值
fn main():
    var l = ListLiteral[Int, String, Float32](1, 'ABC', 3.2)
    print(l.get[0, Int]())
    print(l.get[1, String]())
    print(l.get[2, Float32]())
# 1
# ABC
# 3.2000000476837158
  • VariadicList: 可变参数列表
fn func(*args: Int):
    for i in args:
        print(i, end=' ')

fn main():
    func(1, 2, 3 ,4)
# 1 2 3 4 
  • VariadicListMem: 仅内存类型的可变参数列表
fn make_worldly(inout *strs: String):
    for i in range(len(strs)):
        strs[i] += " world"
        
fn main():
    var s1: String = "hello"
    var s2: String = "konnichiwa"
    var s3: String = "bonjour"
    make_worldly(s1, s2, s3)
    print(s1)  # hello world
    print(s2)  # konnichiwa world
    print(s3)  # bonjour world
  • VariadicPack: 异构可变参数
fn count_many_things[*ArgTypes: Intable](*args: *ArgTypes) -> Int:
    var total = 0

    @parameter
    fn add[Type: Intable](value: Type):
        total += int(value)

    args.each[add]()
    return total

fn main():
    print(count_many_things(5, 11.7, 12))

或者使用 each_idx 在遍历时获取参数的索引

fn count_many_things[*ArgTypes: Intable](*args: *ArgTypes) -> Int:
    var total = 0

    # get element
    print(int(args.get_element[1]()[]))
    @parameter
    fn add[index: Int, Type: Intable](value: Type):
        print('The', index, 'element is', int(value))
        total += int(value)

    args.each_idx[add]()
    return total

内置切片

内置切片包含 Slice 结构体和 slice 函数

Slice

Slice 结构体表示切片表达式。在方括号内使用切片语法时,会创建这种类型的对象,例如

var msg: String = "Hello Mojo"

# 下面两种方式等价
print(msg[6:])
print(msg.__getitem__(Slice(6, len(msg))))

结构体包含三个 Int 字段:

  • start:起始索引
  • end:结束索引
  • step:切片步长

slice

slice 函数与使用冒号的切片语法一样,可以将冒号语法转换为函数形式。例如

var msg: String = "Hello Mojo"

# 下面两种方式相等
print(msg[:6:2])
print(msg[slice(0, 6, 2)])

constrained

实现编译时约束。包含一个 constrained 函数,在编译时检查条件是否为真。

constrained 函数类似于 C++ 中的 static_assert,用于对外层函数引入约束。在 Mojo 中,assert 对函数施加了约束。当断言失败时会显示消息。

该函数包含两个 parameters:

  • condBool 类型的检查条件
  • msgStringLiteral 类型,用于在发生错误断言是打印的消息

使用方式如下:

constrained[1 < 2, "1 < 2"]()

断言为真不会输出任何东西,断言为假时,引发异常并输出字符串信息

DType

实现了 DType 结构体,并定义了很多类型,及对这些类型进行操作的方法。

例如

fn main() raises:
    var s = DType.float16
    if s.is_floating_point():
        print('is floating point')
        @parameter
        fn func[num: DType]():
            print(num)
        s.dispatch_floating[func]()
    else:
        print('bitwidth', s.bitwidth())
        print('size of', s.sizeof())
# is floating point
# float16

range

range 函数与 Python 中的 range 基本一样,用于创建一个范围值。例如

for i in range(10):
    print(i, end=' ')
print()
for i in range(3, -3, -1):
    print(i, end=' ')
# 0 1 2 3 4 5 6 7 8 9 
# 3 2 1 0 -1 -2 

reverse

提供一个反转函数 reversed,可以对 ListDict 和符合 ReversibleRange 特性的集合进行反向迭代。例如,反向遍历 List

for i in reversed(List(1, 2, 3, 4)):
    print(i[], end=' ')
# 4 3 2 1 

或者对 Dict 进行反向遍历

fn main() raises:
    var dict = Dict[String, Int]()
    dict['a'] = 1
    dict['b'] = 2
    for i in reversed(dict):
        print(i[])
# b
# a

print

print 函数用于打印变量的字符串表示,我们前面已经用了很多次了,常用的两个参数为 end 和 sep。例如

fn main() raises:
    print(1, 2, 3, sep=',', end=';')
# 1,2,3;

其中 sep 用于设置多个输出变量之间的间隔符,默认为空格,end 设置打印完变量之后最后添加的字符,默认为换行符

字符串对象方法

String 结构体

String 是一种可变的字符串类型,包含很多常用的实例方法。

例如,连接两个字符串

var a = String('ABC')
print(a[2])   # C
var b: String = 1
print(a + b)  # ABC1

使用连接符连接字符串

var delim = String(',')
print(delim.join[3]((1, 2, 3)))
# 1,2,3

统计字符出现次数,以及出现位置

var a = String('ATCGGGAC')
print(a.count('A'))
print(a.find('A'))
print(a.rfind('A'))
# 2
# 0
# 6

拆分字符串

var a = String("Hello Mojo!")
for s in a.split(' '):
    print(s[])
# Hello
# Mojo!

替换字符串

print(a.replace('Mojo', 'World'))
# Hello World!

删除字符串前后的空白符

var a = String("\t  Hello Mojo ")
print(a.strip())   # 'Hello Mojo'
print(a.rstrip())  # '	  Hello Mojo'
print(a.lstrip())  # 'Hello Mojo '

也可以指定要删除字符串前后字符

print(String('TestHook').removeprefix('Test'))
# 'Hook'
print(String('BaseTestCase').removeprefix('Test'))
# 'BaseTestCase'
print(String('TestHook').removesuffix('Hook'))
# 'Test'
print(String('BaseTestCase').removesuffix('Test'))
# 'BaseTestCase'

大小写转换

var a = String("Hello Mojo")
print(a.upper())  # HELLO MOJO
print(a.lower())  # hello mojo

判断字符串的开头和结尾

var a = String("Hello Mojo!")
print(a.startswith('He'))       # True
print(a.endswith('Jo!'))        # False
print(a.startswith('lo', 3))    # True
print(a.endswith('jo', 6, 10))  # True

函数

Mojo 提供了一些处理字符串对象的方法,包括:

  • ord: 返回单个字符的 ASCII
  • chr: 返回 ASCII 码对应的字符
  • atol: 将字符串数值转换为整数
  • isdigit: 输入的 ASCII 码是否对应数值 0-9
  • isupper: 判断 ASCII 码是否为 ABCDEFGHIJKLMNOPQRSTUVWXYZ
  • islower: 判断 ASCII 码是否为 abcdefghijklmnopqrstuvwxyz
  • isspace: 判断 ASCII 码是否为空白字符,比如空格 " "
print(ord('a'))       # 97
print(chr(97))        # a
print(atol('12306'))  # 12306
print(isdigit(100))   # False, 'd'
print(isupper(66))    # True, 'B'
print(islower(100))   # True, 'd'
print(isspace(9))     # True, 9 => ' '

所谓 ASCII 码为用 0-127 分别表示 128 个字符,Mojo 中用一个 SIMD[int8, 1] 类型的值来表示

协程

协程可以暂停执行,保存程序的状态(包括局部变量的值和下一条要执行的指令的位置)。当协程恢复时,执行将从原来的位置继续,并恢复保存的状态。

包含两个结构体

  • Coroutine: 表示协程
  • RaisingCoroutine: 表示可以引发异常的协程

例如,使用协程计算列表中数字之和

async fn sum(owned list: List[Int]) -> Int:
    var total = 0
    for i in range(len(list)):
        total += list[i]
    return total

async fn call_it() -> Int:
    var task: Coroutine[Int] = sum(List(1, 2, 3, 6, 7, 8))
    var res = await task
    return res

def main():
    var res = call_it()()  # 注意两个小括号
    print('🔥 The sum is: ', res)
# 🔥 The sum is: 27

文件读写

文件读写模块包含一个 FileHandle 结构体和一个 open 函数,用于对文件进行操作。使用 open 函数会返回一个 FileHandle 对象。

例如,将字符串写入文件中

fn main() raises:
    var f = open('demo.txt', 'w')
    f.write('Hello Mojo!\n')
    f.write((StringRef('aaaa')))
    f.close()

打开文件可能会引发异常,需要捕获异常或者将为函数添加为 raises

读取文件中的内容

var f = open('demo.txt', 'r')
var string = f.read()
print(string)
f.close()

默认会将文件中的全部内容读取进来,也可以设置需要读取的大小

import os

fn main() raises:
    var f = open('demo.txt', 'r')
    var word1 = f.read(5)
    print(word1)
    _ = f.seek(1, os.SEEK_CUR)
    var word2 = f.read(4)
    print(word2)
    f.close()

f.seek 表示设置读取位置的偏移,相当于我们在查看文件时光标所指示的位置,read 都是从当前光标开始读取的。

前面的例子,我们先读取了 5 个字节,然后将光标往后移动一个字节,再读取后面 4 个字节。

还可以读取最前面与最后面的单词

var f = open('demo.txt', 'r')
_ = f.seek(-4, os.SEEK_END) # os.SEEK_END 文件的终止位置
var last_word = f.read(4)
print(last_word)
_ = f.seek(0, os.SEEK_SET)  # os.SEEK_SET 文件默认起始位置
var first_word = f.read(5)
print(first_word)
f.close()

将文件数据读入指针中,并将存储的字符设置为 ASCII

import os

fn main() raises:
    var file = open("demo.txt", "r")

    # 将 10 个元素添加到指针数据中
    var ptr = DTypePointer[DType.uint8].alloc(10)
    var bytes = file.read(ptr, 10)
    print("bytes read", bytes)

    var first_element = ptr.load(0)
    print(first_element)

    # 跳过两个元素
    _ = file.seek(2 * sizeof[DType.uint8](), os.SEEK_CUR)

    # 再分配 4 个元素
    var ptr2 = DTypePointer[DType.uint8].alloc(4)
    var bytes2 = file.read(ptr2, 4)

    print( ptr2[0], ptr2[3])

还可以使用 read_bytes 来读取数据,它会返回一个 List[SIMD[int8, 1]] 类型的对象,例如

import os

fn main() raises:
    var file = open("demo.txt", "r")
    for ch in file.read_bytes():
        print(ch[], end=' ')
# 72 101 108 108 111 32 77 111 106 111 33 10 97 97 97 97 

或者使用 with 语句自动关闭文件:

with open("my_file.txt", "r") as f:
  print(f.read())

其他

还有一些简单的函数,例如:

  • int:转换为整数类型
  • str:转换为字符串类型
  • bool:转换为布尔值
  • len:计算对象的长度
  • Tuple:由零个或多个以逗号分隔的值组成的元组
var t = Tuple(1, 2, 3)
print(t.get[0, Int]())
# 1
  • hex:返回给定整数的十六进制字符串
  • hash:返回基于底层实现的 64 位整数哈希值。
from random import rand

var n = 64
var rand_bytes = DTypePointer[DType.int8].alloc(n)
rand(rand_bytes, n)
print(hash(rand_bytes, n))
  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

名本无名

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

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

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

打赏作者

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

抵扣说明:

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

余额充值