Reputation: 25133
According to this comment:
You also should not use single quotes in
print ">>${ '_<$filename' }<<\n"
. Instead try:print ">>${ \"_<$filename\" }<<\n"
I always thought that differences between "
and '
are only that the string is interpreted or not.
I want to ask why at this context:
print ">>${ \"_<$filename\" }<<\n"
print ">>${ '_<$filename' }<<\n"
perl prints different values?
Why I should use \"
instead of just '
here?
Upvotes: 4
Views: 624
Reputation: 25133
This is only my assumption and I can not find any official documentation about this, but it seems work.
Also ilmari
says that I should go via the symbol table
:
${$main::{'_<foo::bar::baz'}}
Thus my code is splited on two cases:.
CASE 1:
Devel::Peek::Dump( ${ "::_<$filename" } );
Devel::Peek::Dump( ${ '::_<' ."$filename" } );
Devel::Peek::Dump( ${ "::_<Specio::Constraint::Simple->_optimized_constraint" } );
Devel::Peek::Dump( ${ '::_<Specio::Constraint::Simple->_optimized_constraint' } );
All these are same. Double quoted or single quoted: it has not matter.
SV = PV(0xe64ab00) at 0xe300bc8
REFCNT = 1
FLAGS = ()
PV = 0
CASE 2:
Devel::Peek::Dump( ${ ${ 'main::' }{ "_<$filename" } } );
Devel::Peek::Dump( ${ ${'::'}{ "_<$filename" } } );
Devel::Peek::Dump( ${ $::{ "_<$filename" } } );
Devel::Peek::Dump( ${ $::{"_<Specio::Constraint::Simple->_optimized_constraint"} } );
Devel::Peek::Dump( ${ $::{'_<Specio::Constraint::Simple->_optimized_constraint'} } );
Also it has no matter how it was quoted. Just going via the symbol table I can reach the value:
SV = PV(0x15e0a40) at 0x186cd10
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0x1934880 "Specio::Constraint::Simple->_optimized_constraint"\0
CUR = 49
LEN = 51
findings
It has no matter how to quote variable name. Only difference is how we go:
Thus if you variable name has not double colon, then you can use either form:
Devel::Peek::Dump( ${ $::{ "$name" } } );
Devel::Peek::Dump( ${ "::$name" } );
both will refer the same thing.
But if your variable name has double colon, then you must access its value only via symbol table
UPD
Some one may note that this answer does not answer my question directly. You are right.
I post test results of two different method of accessing variable's value here because I count these findings important.
Upvotes: 0
Reputation: 385809
The answer is quite simple: A $
and @
character has no special significance when found in code in a string literal.
">>${ \"_<$filename\" }<<\n"
is equivalent to ">>" . ${ "_<$filename" } . "<<\n"
.
">>${ '_<$filename'" }<<\n"
is equivalent to ">>" . ${ '_<$filename' } . "<<\n"
.
As you can see $filename
wasn't replaced with the value of $filename
as would normally happen in double-quoted strings.
If that's not clear, let's consider a far more common example.
">>$h{$key}<<"
is equivalent to ">>" . $h{$key} . "<<"
.
">>$h{\"$key\"}<<"
is equivalent to ">>" . $h{"$key"} . "<<"
.
">>$h{'$key'}<<"
is equivalent to ">>" . $h{'$key'} . "<<"
.
Now consider what would happen if $key
was interpolated instead. (For example, use $key = "2+3";
.) The behaviour would be far more unexpected and far more dangerous.
Similarly, a \
character has no special significance when found in code in a string literals unless it escapes a delimiter.
"foo ${\f()} bar"
is equivalent to "foo " . ${\f()} . " bar"
As you can see \f
wasn't replaced with a form feed as would normally happen in double-quoted strings literals.
Upvotes: 3
Reputation: 6626
What happens is that in both cases, $filename
is not interpolated by the outer string (">>...<<\n"
), but rather by ${ ... }
. (this is just deduction from the behavior). This means that in ">>${ '_<$filename' }<<\n"
, $filename
isn't interpolated at all, while it is in ">>${ \"_<$filename\" }<<\n"
.
If it was interpolated by the outer string, then you'd ran into some troubles if your string was containing quotes:
$filename = "ab'cd";
If interpolation was done by the outer string, then "${'$filename'}"
would be equivalent to "${'ab'cd'}"
, which is a syntax error. Or maybe rather to "${ab'cd}"
, which is equivalent to "${ab::cd}"
; not what you want either.
Whereas if the interpolation is done by ${}
, then in "${'$filename'}"
, ${...}
is really ${"$filename"}
(without the escaped double-quotes), which interpolates $filename
, and you get something like ${"ab'cd"}
, like you'd want.
Consider this example:
$abcd = "First var";
${'$v'} = "Second var";
$v = "abcd";
say "${\"$v\"}"; # prints "First var"
say "${'$v'}"; # prints "Second var"
It shows that with "${\"$v\"}"
, $v
was interpolated, whereas with "${'$v'}"
, it wasn't.
Upvotes: 2
Reputation: 1019
The short answer to your question is that $filename
is interpolated in your first example, whereas it's not in the second example. Why, you ask? Oh, boy. Strap in for a bumpy ride.
First, understand what the ${...}
notation is for. There are two reasons to use it: one is to simply distinguish a variable name from surrounding text; the other is to dereference the value returned by a BLOCK (of code).
If the contents of the brackets are just a bareword (with possible surrounding whitespace), then it's just a simple variable name. Thus the string "$foo"
and the string "${foo}"
and the string "${ foo }"
all produce the same result. This is handy if you need to append text directly after a variable in a string: instead of saying $foo.'bar'
, you can say "${foo}bar"
. The notations work outside interpolating strings too: you can say ${foo} = 'bar'
if you like.
But that's not what your code is doing. Oh, no. You're aiming the double-barrel shotgun of symbolic dereferencing directly at your feet. You're evaluating the contents of the brackets as code, and using the result as a reference to a variable. You're making it doubly confusing by putting the whole lot in an interpolating string. Let's not go any deeper than necessary with the concept of dereferencing, since that's not the main question here. The key point is that the stuff in curly brackets is effectively being eval'd if it's not a bareword.
Now, understand that when Perl reaches the "${" notation inside your interpolating string, it has to make a decision about what it's interpolating here: an ordinary variable name or code. Without interpolating anything else, it seeks the matching close-bracket. If the sub-string delimited by those brackets doesn't look like a bareword, then it's going to be eval'd. You still need to escape double-quotes in this region, because any unescaped double quote would count as the final delimiter for the outer string of which this is a subset, and the remainder of your "block" would be outside the string.
With that in mind, we can see that your first example evals "_<$filename"
, whereas your second example evals '_<$filename'
. That's just the difference between an interpolating string and not: the first has $filename
replaced by something, whereas the second one is strictly literal.
In both cases, the string is used to perform symbolic dereferencing on a global scalar variable with a ghastly name, and shame on you if you actually defined it. The less said about it, the better.
If you think I'm kidding about the contents of the curly brackets being effectively eval'd try this code.
print "This, ${die qq(oops!\n)}, and this.\n";
Of course, the key difference between in-string code like this and eval()
proper is that eval()
would catch the exception and set $@. This doesn't: it just dies as it would in an ordinary code block.
This gives rise to one of Perl's hidden tricks. You can't interpolate a method call like $foo->bar
, but you can dereference a reference to an array that contains it, like so.
print "@{[$foo->bar]}\n";
In summary then, for the love of God please don't write code like that.
Upvotes: 6