@符号

在Z1h中, @符号有丰富的作用, 且完全不同于其他主流语言的@已知用法. 如果应用得当, 可以大大简化代码, 提高开发效率. 以及可以起到很多特殊的效果, 例如异常处理、断点debug等等.

便捷函数

先上一段示例代码:

// 声明一个便捷函数
z1h.at(e=>{
	print('左值为:', e)
});
print('结果为:', 100@ + 123@)

// 输出结果如下
// 左值为: 100
// 左值为: 123
// 结果为: 223

可以看到, 通过调用z1h.at, 将一个函数设为便捷函数之后, 就可以使用@符号了.

简单地说, @符号的作用就是: 调用便捷函数, 并将左值作为参数传入

特性如下:

  1. 结合优先级最高, 即1/0@实质上等价于1/(0@), 而(1/0)@
  2. 即使@的左值抛出异常, 也仍然会执行便捷函数
  3. 默认情况下, 便捷函数的运行结果不会影响函数调用处的值, 但是运行过程抛出的异常会传递
  4. 可以声明带别名的便捷函数, 并且别名的任意前缀均可触发该便捷函数
  5. 便捷函数的作用域为当前代码块, 通过指定参数也可以声明到全局作用域

接下来分点介绍:

特性1, 结合优先级

运行代码

// 声明一个别名为foo的便捷函数
z1h.at("foo", e => {
	print("foo:", e)
});
// 通过 xxx@f / xxx@fo / xxx@foo 均可运行别名为foo的便捷函数
2 * 200 @f;  // 200
(2 * 200)@f; // 400
2 * (200@f); // 200

可知, @符号的优先是最高的

特性2, 可以处理异常

示例代码:

// 便捷函数有3个参数, 分别是: [左值, 异常, 上下文]
z1h.at((val, err, ctx) => {
	if (err) {
		print('抛出了异常 %v, 代码为 %s'.fmt(err, ctx.code))
	} else {
		print('顺利执行, 左值为 %v, 代码为 %s'.fmt(val, ctx.code))
	}
});

(100 / 20) @ // 顺利执行, 左值为 5, 代码为 100 / 20
(100 / 0) @  // 抛出了异常 .... division by zero, 代码为 100 / 0

上下文是一个包含了很多特殊函数的对象, 例如可以将抛出的异常捕获、设置返回值、获取运行的代码、延时执行等等

z1h.at((val, err, ctx) => {
	var e = ctx.recover() // 捕获并销毁异常
	if (e) {
		print('捕获了异常:', e.Error())
		ctx.result(1000) // 有异常的情况下, 设置返回值为1000
	} else {
		print('顺利执行')
	}
});

print('结果为:', (100/20)@ + (100/0)@) // 结果为: 1005

特性3, 返回值

便捷函数无论return了什么值, 都不会取代调用处的原值, 但是如果调用上下文对象的result函数, 即可设置返回值

z1h.at('foo', e => {
	return 100 // 这种方式不会修改到值
});
z1h.at('bar', (_, _, ctx) => {
	ctx.result(200) // 设置返回值
});
var a = 500@f; // foo函数
var b = 600@b; // bar函数

print({a, b}) // {"a":500,"b":200}

特性4, 别名

上文特性1中已经演示了便捷函数的别名用法, 在调用z1h.at的时候, 如果第一个参数是字符串, 就可以为该便捷函数设置别名, 例如

z1h.at("helloworld", e => print('Hello,', e))

那么接下来就可以用 值@helloworld 的方式来调用便捷函数了

并且可以使用任意前缀调用, 例如 值@hello 甚至 值@h

123@hello
1@h

注意, 如果别名前缀有重复的, 会起到覆盖的作用, 例如:

z1h.at("helloworld", e => print('Hello,', e));
z1h.at("hi", e => print('Hi,', e));

1@h  // 调用的是后声明的hi
1@he // 调用的是helloworld
1@hi // 调用的是吻合前缀的hi

特性5, 作用域

运行以下代码

z1h.at("hi", e => print('Hi outside,', e));

111@h;
{
	z1h.at("hi", e => print('Hi inside,', e));

	222@h;
}
333@h;

你将会看到输出为:

Hi outside, 111
Hi inside, 222
Hi outside, 333

也就是inside的便捷函数不会影响到代码块之外的333

如果想要指定一个全局作用域的便捷函数, 将z1h.at的最后一个参数设为1即可

// 如果在这里声明wow, 优先级高于全局作用域, 会干扰理解
{
	z1h.at("wow", e => print('Wow inside,', e), 1);

	111@w;
}
222@w;

输出:

Wow inside, 111
Wow inside, 222

此时, 代码块之外的222也被影响了

内置便捷函数

z1h的运行时内置了几个便捷函数, @ = assert; @s = string; @t = time

示例代码:

$db.find('test')@ // 等同于assert($db.find('test'))

123@s + 456       // 输出123456而非579, 因为@s将左值转为了字符串类型

"20210119"@t      // 输出了一个Time对象

// 结合以上, 在数据库中查找2021年之前过期的会员用户数
$db.count('user', {
	vip: {$lt: 2020@s@t.fmt()}	
})@