I need to populate a Hash with various values. Some of values are accessed often enough and another ones really seldom.
The issue is, I’m using some computation to get values and populating the Hash becomes really slow with multiple keys.
Using some sort of cache is not a option in my case.
I wonder how to make the Hash compute the value only when the key is firstly accessed and not when it is added?
This way, seldom used values wont slow down the filling process.
I’m looking for something that is “kinda async” or lazy access.
There are many different ways to approach this. I recommend using an instance of a class that you define instead of a Hash. For example, instead of…
… make your own class and define methods, like this…
If you want a simple way to cache the long computations or it absolutely must be a Hash, not your own class, you can now wrap the Config instance with a Hash.
One issue with the above example is that
h.keyswill not include:barbecause you haven’t accessed it yet. So you couldn’t, for example, iterate over all the keys or entries inhbecause they don’t exist until they’re actually accessed. Another potential issue is that your keys need to be valid Ruby identifiers, so arbitrary String keys with spaces won’t work when defining them onConfig.If this matters to you, there are different ways to handle it. One way you can do it is to populate your hash with thunks and force the thunks when accessed.
One caveat with this last example is that it won’t work if your values are callable because it can’t tell the difference between a thunk that needs to be forced and a value.
Again, there are ways to handle this. One way to do it would be to store a flag that marks whether a value has been evaluated. But this would require extra memory for every entry. A better way would be to define a new class to mark that a Hash value is an unevaluated thunk.
The downside of this is that now you have to remember to use
Unevaluted.newwhen populating your Hash. If you want all values to be lazy, you could override[]=also. I don’t think it would actually save much typing because you’d still need to useProc.new,proc,lambda, or->{}to create the block in the first place. But it might be worthwhile. If you did, it might look something like this.So here is the full code.