Search code examples
perldictionaryreferenceargumentssubroutine

Perl: transfer list of huge strings to subroutine without their copying


The task is to transfer a list of huge strings to subroutibe, but avoiding their copying on transfer. Say I have a reference named $ref pointing to the very large string. Also let's have f($) subroutine accepting a single argument. There are no problem to transfer this string to f:

f($$ref); # data pointed by $ref is not copied to temporary value here

Really I have not a single string, but list of them, let's assign them to @a:

my @a = ($ref_1, $ref_2, $ref_3, ...);

Now the problem would be solved by

f(map {$$_} @a);

but map does copy every dereferenced item from @a, and then transfer those copied instances to f.

I have no control on f since it actually is the method from CPAN module.

So is there possible to solve the task? Much thanks in advance.


Solution

  • Yes, 'map' can be a bit annoying in the way it always copies.

    You can dereference a all elements from entire list into an array, without copying, using Data::Alias::deref.

    Assuming @a is an array of references, and you want to call a function f() with an argument list which is the result of dereferencing those references, then you can do

    use Data::Alias qw( alias deref );
    
    f(deref @a);
    

    (Note that Data::Alias exists as a distro module for (for example) Ubuntu (libdata-alias-perl), so you should be able to use it even if you can't use CPAN directly.)

    In fact, if you're dealing with a lot of large string objects and minimising copying is a concern, you might want to use Data::Alias more extensively. In fact, once you have Data::Alias in your programming arsenal, you might find you don't need to store the data in your array as references at all.

    Say, your data comes to you one (huge) value at a time, and you want to put those values into an array. Whereas you might currently do

    push @a, \$value;
    

    you could change that to

    alias push @a, $value;
    

    If you have two lists (of huge elements) that you want to make into one big array, you can do this

    alias my @one_big_array = (@a, @b);