欢迎光临散文网 会员登陆 & 注册

流程控制、传递引用:Scarpet中部分语句的作用

2019-07-22 01:15 作者:禄存天玑  | 我要投稿

作者:禄存,题图来自推特画师@tkmiz ,侵删

Intro.

在写出了优化又差又难读的宽搜和A*之后,我又参考网上的资料写了一个Heap(堆)结构和一个HashMap(哈希表)结构。在编写的过程中发现了一些和技巧,和大家分享一下,也作为对之前文章的修正。

另外我昨天在 Github 上创建了存放自己写的 Scarpet 脚本的仓库,欢迎大家来交流学习。

字符串传引用

之前说过,Scarpet里的自定义函数都是只能传值,不能传引用的(正所谓byVar和byRef的区别)。但是官方这里提供了一个函数:var(expr)。官方解释翻译过来是:

返回表达式的字符串值所代表的变量,允许更加类似编程地操作变量。可以像HashMap的键-值一样使用本地变量。也能用在全局变量上。

官方的例子:

a = 1; var('a') = 'foo'; a => a == 'foo'

也就是说,函数会根据字符串找到作用范围内的变量引用,传引用还是可能的,不过要做的是传入变量名字符串,然后在方法内部调用var(expr),例如我做的一个简单的根据下标获取Heap中元素的方法:

heap_get(heap_name, index) -> element(var(heap_name), index);

heap_get方法会在其它的很多函数里被调用到,而这些函数接受的也是字符串的变量名。字符串逐层传递到这里再被统一转换成对变量的正式引用,获取下标对应的值后再逐层传回去。

不过要注意的是,这种传引用方式只对全局变量有效t(g) -> var(g) = 16; a = 0; t('a'); a的输出是0,但是t(g) -> var(g) = 16; global_a = 0; t('global_a'); global_a的输出是16

如图所示

这么一来,传递引用就在某种程度上成为了可能,程序库也能正常地执行。

顺便一提,还有vars(prefix)能返回包含前缀prefix的所有变量名(字符串),以及undef(expr)可以根据表达式字符串内容移除变量或者函数。这两者我还没接触过,但应该也有不小的作用。

自带break功能的循环语句

还记得第一篇文章里说过,Scarpet 语言是不存在breakcontinue语句的。但是作者给firstfor语句设置了退出功能。例如first(list,expr(_,_i))的官方解释:

寻找并返回列表中首个能满足表达式exprexpr == true == 1)的值。_被定义为当前元素的值,_i被定义值的下标。

官方的例子:

first(range(1000,10000), n=_; !first( range(2, sqrt(n)+1), !(n % _) ) ) => 1009 //1000之后的第一个素数

注意这个例子,我们需要重命名外部first中的_来在内部的first中调用。

虽然这条语句一般只是用来在查找列表的元素,但是它的功能暗示了可以通过在特定情况下返回true,而在另一些情况下返回false,可以使流程提前中断

例如在HashMap中,删除键值的函数需要搜索哈希码(hashcode)对应的保存键-值对的列表。这部分是这样写的:

'前面是一些检查键值是否合理的语句'; 

first(list,

   if (element(_, 0) == key,

       '这里会有一些删除操作';

       true

) != null,

先用first()遍历列表,一旦列表中键-值对的键与要查询的键匹配,进行删除操作,返回一个true来提前中断循环。如果键不匹配就继续查找。

另外,因为first会在未查询到的情况下返回一个null,在最后进行检测就可以判断是否成功进行了一次删除。这里要注意的是:虽然null会被看作false,但是如果第一个元素就查找成功,返回的0也会被误判成false,所以必须要对null加以判断。

同样的,for(list,expr(_,_i),exit(_,_i)?)把退出判断放到了第三个参数上,是可选的。for会返回成功迭代的次数;而first返回找寻到的列表元素,找不到返回null。因为两者的性能相差无几,可以自行根据功能、易读性和编程习惯进行选用。例如我在堆查找里就使用了for循环:

heap_search(heap_name, node) -> (

   result = -1;

   for (var(heap_name), , if (compare_node(_, node) == 0, result = _i; true, false));

   result

);

一旦查找到,就把结果设置成当前下标并退出。这样就很简单地实现了在查询到的情况下返回下标,否则返回-1的功能。

这样一来,既可以规避使用return()带来的性能损耗(上一篇文章说过),又可以相对简洁地对循环的流程进行控制。

流程控制、传递引用:Scarpet中部分语句的作用的评论 (共 条)

分享到微博请遵守国家法律