How can I use an array as the arguments to a builtin function (sprintf, join, etc.) in Perl?
Asked Answered
G

2

6

I have an array like the following:

my @a = ('%d %d', 10, 20);

I'd like to use the elements of this array as the arguments for a function.

If I just try using it directly, it gets interpreted in scalar context:

say sprintf(@a);       #=> 3

I also tried this, which didn't work of course:

@_ = @a; say sprintf;  #=> Not enough arguments...

The splat syntax that some languages have doesn't work here either (I'm just taking wild guesses at this point):

say sprintf(*@a);      #=> (error)

The same problems occur with other builtin functions, such as join.

Is there any way to take an arbitrary array (of unknown length) and an arbitrary function and call the function using the elements of the array as arguments for the function? If so, how?

Gyrostat answered 24/11, 2015 at 15:38 Comment(2)
Related: Why do printf and sprintf behave differently when only given an array?Normalcy
Yet another reason why perl prototyping can be really wierd.Surd
R
8

sprintf has prototype $@, not @, so the first argument passed to it is evaluated in scalar context.

Prefixing a sub call with & ignores the prototype, but you must use the full name of builtins for them to be sub calls:

say &CORE::sprintf(@a); # Perl 5.16+

Or pass the first argument explicitly:

say sprintf($a[0], @a[1..$#a]);
Rappee answered 24/11, 2015 at 15:41 Comment(3)
...or if all you're doing is printing, use printf, which does use the 0th element of the array as the format string.Normalcy
@Normalcy ... and add say ''; too, for the final newline.Rappee
Ah, I probably should have tested the first attempt with more than just one function... (although some others such as join exhibit similar behavior). Thanks!Gyrostat
D
1

The sprintf operator behaves like a sub with the $@ prototype.

$ perl -Mv5.14 -e'say prototype("CORE::sprintf") // "[none]"'
$@

As such, @a is being evaluated in scalar context. This produces the number of elements within.

Destructive solution:

my $format = shift( @a );
sprintf( $format, @a )

Non-destructive solution:

sprintf( $a[0], @a[ 1 .. $#a ] )
Dodecasyllable answered 31/8, 2023 at 23:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.