Kirby
Kirby

Reputation: 3107

Concatenate with non-variable stuff in perl

I need to concate map result with a string.

perl -le 'print (map { (q(a)..q(z))[rand(26)] } 1..3) . "123"'

Expected 3 random symbols and 123. But there is no 123 just 3 random symbols. In general, I need to add a variable there.

Upvotes: 1

Views: 132

Answers (4)

ikegami
ikegami

Reputation: 385764

With warnings:

print (...) interpreted as function at -e line 1.
Useless use of concatenation (.) or string in void context at -e line 1.

This is because your code is of the following form:

print(...) . "123"

Solutions:

perl -le'print( map( { (q(a)..q(z))[rand(26)] } 1..3 ) . "123" )'   # Fully parenthesized
perl -le'print map( { (q(a)..q(z))[rand(26)] } 1..3 ) . "123"'      # Opt parens dropped
perl -le'print( ( map { (q(a)..q(z))[rand(26)] } 1..3 ) . "123" )'
perl -le'print +( map { (q(a)..q(z))[rand(26)] } 1..3 ) . "123"'    # "Disambiguated"

Except those aren't right either. While they fix the problem you asked about, they reveal a second problem. They invariably print 3123 because map in scalar context returns the number of scalars it would otherwise return in list context.

Solutions:

perl -le'print( map( { (q(a)..q(z))[rand(26)] } 1..3 ), "123" )'         # . => ,
perl -le'print map( { (q(a)..q(z))[rand(26)] } 1..3 ), "123"'            # . => ,
perl -le'print( ( map { (q(a)..q(z))[rand(26)] } 1..3 ), "123" )'        # . => ,
perl -le'print +( map { (q(a)..q(z))[rand(26)] } 1..3 ), "123"'          # . => ,
perl -le'print join "", ( map { (q(a)..q(z))[rand(26)] } 1..3 ), "123"'  # join

Upvotes: 3

Dave Cross
Dave Cross

Reputation: 69244

There are a couple of interesting things going on here. First let's ask Perl to help up track down any problems by turning on warnings.

$ perl -Mwarnings -le 'print (map { (q(a)..q(z))[rand(26)] } 1..3) . "123"'
print (...) interpreted as function at -e line 1.
Useless use of concatenation (.) or string in void context at -e line 1.
lfy

Two warnings there. Let's look at both of them.

print (...) interpreted as function

If the first non-whitespace character following print (or any other list operator) is a opening parenthesis, then Perl assumes that you want to call print as a function and it will look for the balancing closing parenthesis to end the list of arguments to print.

Useless use of concatenation (.) or string in void context

Because the print call is assumed to end with the closing parenthesis, the . "123" isn't doing anything useful. And is therefore ignored.

The standard way to tell Perl that an opening parenthesis isn't marking a function call is to use a +.

$ perl -Mwarnings -le 'print +(map { (q(a)..q(z))[rand(26)] } 1..3) . "123"'
3123

Well, we lost the warnings. But we got '3' where we were hoping to see three symbols. What we have here now is basically this:

print +(map ...) . "123";

Because of the concatenation, map is being called in scalar context. And in scalar context, map no longer returns a list of values, but the size of that list (an integer - 3 in this case).

The fix for that is to replace the . with a comma, so map is called in list context.

$ perl -Mwarnings -le 'print +(map { (q(a)..q(z))[rand(26)] } 1..3), "123"'
ntg123

So you were being burnt by a) the parentheses not doing what you wanted them to do and b) map being called in scalar context.

Upvotes: 2

Toto
Toto

Reputation: 91385

Enclose the whole line to be printed inside parenthesis and use a comma as separator:

perl -le 'print ( (map { (q(a)..q(z))[rand(26)] } 1..3) , "123")'

Upvotes: 0

Mike
Mike

Reputation: 2005

perl -le 'print join("",map { (q(a)..q(z))[rand(26)] } 1..3) . "123"'

Upvotes: 0

Related Questions