我已经开发了a module for processing a collection of documents。
运行一次软件就能收集到关于它们的信息。数据存储在称为%processed和%symbols的两个结构中。需要对数据进行缓存,以便随后在同一组文档上运行软件,其中一些文档可以更改。(文档本身使用CompUnit modules进行缓存。
当前数据结构的存储/恢复如下:
# storing
'processed.raku`.IO.spurt: %processed.raku;
'symbols.raku`.IO.spurt: %symbol.raku;
# restoring
my %processed = EVALFILE 'processed.raku';
my %symbols = EVALFILE 'symbols.raku';将这些结构输出到文件中可能会很慢,因为哈希会被解析来创建Stringified表单,而输入会很慢,因为它们正在重新编译。
它不打算检查缓存的文件,只是为了保存软件运行之间的状态。
此外,尽管这对我的用例来说不是问题,但这项技术通常不能使用,因为据我所知,Stringification (序列化)不适用于Raku闭包。
我想知道是否可以使用CompUnit模块,因为它们用于存储模块的编译版本。那么,它们也许可以用来存储数据结构的“编译”或“内部”版本?
已经有办法做到这一点了吗?
如果没有,有没有什么技术上的原因呢?
发布于 2021-02-25 20:45:18
(您很有可能已经尝试过这个方法,并且/或者它不适合您的用例,但我想我应该提一下,以防它对您或其他发现这个问题的人有帮助。)
您是否考虑过使用JSON::Fast将数据序列化到JSON和JSON之间?它已经针对(反)序列化速度进行了优化,以一种基本的字符串化还没有/不能做到的方式。这不允许存储Block或其他闭包-正如您所提到的,Raku目前没有一个很好的方法来序列化它们。但是,既然您提到这不是问题,那么JSON就有可能适合您的用例。
[编辑:正如您在下面指出的,这可能会使对某些Raku数据结构的支持变得更加困难。通过将数据类型指定为序列化步骤的一部分,通常(但并非总是)可以解决此问题:
use JSON::Fast;
my $a = <a a a b>.BagHash;
my $json = $a.&to-json;
my BagHash() $b = from-json($json);
say $a eqv $b # OUTPUT: «True»对于更难用JSON表示的数据结构(比如那些具有非字符串键的数据结构)来说,这会变得更加复杂。JSON::Class模块也可能会有所帮助,但我还没有测试它的速度。]
发布于 2021-03-03 16:28:53
在看了其他答案和预编译代码后,我意识到我最初的问题是基于一个误解。
Rakudo编译器生成一个中间的“字节码”,然后在运行时使用。由于模块是用于编译目的的自包含单元,因此可以预编译它们。这个中间结果可以被缓存,从而极大地提高了Raku程序的速度。
当Raku程序use已经编译的代码时,编译器不会再次编译它。
我曾认为预编译缓存是程序内部状态的一种存储,但事实并非如此。这就是为什么--我想--@拉尔夫被这个问题搞糊涂了,因为我没有问对问题。
我的问题是关于数据的存储(和恢复)。JSON:: fast,正如@codesections所讨论的,它非常快,因为它是由Rakudo编译器在较低级别使用的,因此是高度优化的。因此,在恢复时重构数据将比恢复本机数据类型更快,因为较慢的速率决定步骤是从“磁盘”存储和恢复,而JSON执行得非常快。
有趣的是,我提到的CompUnit模块使用了低级JSON函数,这使得JSON::Fast变得如此快速。
我现在正在考虑使用优化的例程存储数据的其他方法,可能是使用压缩/归档模块。这将归结为最快的测试。JSON路由可能是最快的。
所以这个问题没有明确的答案,因为问题本身是“不正确的”。
发布于 2021-02-28 08:35:46
更新 @RichardHainsworth指出,我被他们的问题搞糊涂了,尽管我觉得这样回答应该会有帮助。根据他的反应,以及他决定不接受@codesection的答案,这是当时唯一的另一个答案,我得出结论,最好删除这个答案,以鼓励其他人回答。但现在Richard已经提供了一个很好的解决方案,我正在删除它,希望它现在更有用。
TL;DR不使用EVALFILE,而是将您的数据存储在一个模块中,然后使用use。有一些简单的方法可以做到这一点,这些方法对EVALFILE的改进最少,但却很有用。还有更复杂的方法可能会更好。
对EVALFILE的一个小改进
我决定首先提出一个小的改进,这样你就可以巩固你在EVALFILE中的思维转变。它在两个方面都很小:
与EVALFILE.相比,
我建议您在真正实现我在第一节中描述的内容之前,适当地考虑这个答案的其余部分(它描述了更复杂的改进,可能会带来更大的回报,而不是这个小的改进)。我认为这一小的改进很可能被证明是多余的,而不仅仅是作为通往后面部分的心理桥梁。
编写一个程序,比如store.raku,创建一个模块,比如data.rakumod
use lib '.';
my %hash-to-store = :a, :b;
my $hash-as-raku-code = %hash-to-store .raku;
my $raku-code-to-store = "unit module data; our %hash = $hash-as-raku-code";
spurt 'data.rakumod', $raku-code-to-store;(当然,我对.raku的使用过于简单。以上只是一种概念证明。)
这种形式的数据写入基本上与您当前的解决方案具有相同的性能,因此在这方面没有任何好处。
接下来,编写另一个使用它的程序,比如using.raku:
use lib '.';
use data;
say %data::hash; # {a => True, b => True}在模块中使用use需要编译它。因此,当您第一次使用这种方法而不是EVALFILE读取数据时,它不会更快,就像写入数据一样。但是对于后续的读取,它应该要快得多。(直到您下一次更改数据并必须重新构建数据模块。)
这一节也不涉及闭包串行化,这意味着你仍然在做一个可能不必要的数据写入阶段。
强化闭包;黑客攻击
可以扩展上一节的方法以包括闭包的字符串。
您只需要访问包含闭包的源代码;使用regex/parse提取闭包;然后将匹配写入数据模块。简单!;)
现在,我将跳过填写细节,部分原因是我再次认为这只是一座心理桥梁,并建议您继续阅读,而不是像我刚刚描述的那样尝试。
使用CompUnits
现在我们到达以下位置:
我想知道是否可以使用CompUnit模块,因为它们用于存储模块的编译版本。那么,它们也许可以用来存储数据结构的“编译”或“内部”版本?
我对你在这里问的问题有点困惑,原因有两个。首先,我认为您指的是文档(“文档本身是使用CompUnit模块缓存的”),并且文档被存储为模块。其次,如果您的意思是文档存储为模块,那么为什么不能存储您想要存储在其中的数据呢?您是否担心隐藏数据?
无论如何,我假定您正在询问有关在文档模块中存储数据的问题,并且您对“隐藏”该数据的方法感兴趣。
一个简单的选择是像我在第一节中所做的那样编写数据,但是在实际文档之后的末尾插入our %hash = $hash-as-raku-code"; etc代码,而不是在开头。
但也许这太丑陋了/还不够“隐藏”?
另一种选择可能是在文档模块的末尾添加带有Pod block configuration data的Pod块。
例如,将所有代码放入一个文档模块中,并抛出一个say作为概念验证:
# ...
# Document goes here
# ...
# At end of document:
=begin data :array<foo bar> :hash{k1=>v1, k2=>v2} :baz :qux(3.14)
=end data
say $=pod[0].config<array>; # foo bar也就是说,这只是在模块中执行的代码;我不知道模块的编译形式是否保留了配置数据。此外,您还需要使用"Pod loader“(参见Access pod from another Raku file)。但我猜你对这些事情都很了解。
同样,这可能还不够隐蔽,而且还存在一些限制:
在Hashs.或中,
Str、Int、Num或Bool类型的文字标量,或者是它们的聚合\ns的双引号字符串。)修改Rakudo
Aiui,假设RakuAST登陆,编写可以使用Raku模块执行任意工作的Rakudo插件将相对容易。从RakuAST宏到基本的is parsed宏似乎只有一小步之遥,而在编译器中提取源代码(例如闭包的源代码),然后将其作为数据输出到编译后的代码中似乎也是一小步,可能会附加到Pod声明符块中,而这些块又会作为代码附加到代码中。
因此,也许只需等待一两年,看看RakuAST是否会登陆并获得您需要的钩子,以便通过Rakudo完成您需要做的事情?
https://stackoverflow.com/questions/66368049
复制相似问题