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
:
cond
:Bool
类型的检查条件msg
:StringLiteral
类型,用于在发生错误断言是打印的消息
使用方式如下:
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
,可以对 List
、Dict
和符合 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
函数用于打印变量的字符串表示,我们前面已经用了很多次了,常用的两个参数为 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))