So, as I read it...
>>> def setX(arg):
arg="Two"
>>> val="One"
>>> setX(val)
>>> val
'One'
...is like...
sub setX { my ($arg) = @_; # Takes a value
$arg="Two"; # Sets the value
} # Ends, without doing anything external
my $val="One"; # Puts a string-looking thing into the $val scalar
setX(\$val); # Calls setX() with the \reference\ to the $val scalar
# Within setX: $arg is a scalar with something like "SCALAR(0x183f0d4)"
# Then, $arg becomes "Two";
# Meanwhile, location "SCALAR(0x183f0d4)" is unchanged
print "$val\n"; # Outputs $val (from "SCALAR(0x183f0d4)", or wherever it is).
One
(I'd do that in C, but Perl is open-typed enough to make it meaningful that a variable is a scalar which is a string which is actually a reference-code to another memory location, but C would comlain mightily unless I wrote some support-code.)
>>> def setY1(arg):
arg.append('spam')
arg.remove('juice')
>>> def setY2(arg):
arg = ['milk', 'cereal']
# arg references a new list now, so the passed reference is ignored
>>> thelist = ['eggs', 'juice']
>>> setY1(thelist)
>>> thelist
['eggs', 'spam']
>>> setY2(thelist)
>>> thelist
['eggs', 'spam']
c.f. with
sub SetY1a { my ($arg) = @_; # Takes a value (assumed to be a reference to an array)
push @$arg, 'spam'; # 'Append' the item "spam".
# arg.remove('juice') # Trickier, with an array, but one way of doing it may be...
@$arg = map {($_ eq 'juice')?():$_;} @$arg; # (But I'd want to test that, ideally. ;)
} # End of SetY1a (array referencing code)
sub SetY1h { my ($arg) = @_; # Takes a value (assumed to be a reference to a hash)
$$arg{'spam'}++; # Just set an item for 'spam' (or increment, if one already exists)
delete $$arg{'juice'}; # If we /had/ a 'juice' item, we don't any more, although alternatively we could...
# if (defined($$arg{'juice'}) && !(--$$arg{'juice'})) { delete $$arg{'juice'} }
# If it exists, decrement the number,
# and if that decrement made it zero, delete it, for neatness
# Of course, hashes don't allow ordering (like ['bacon', 'eggs', 'eggs'] as distinct from ['eggs', 'bacon', 'eggs'];
} # Anyway, end of SetY1h (hash-referencer)
sub setY2 { my ($arg) = @_; # This one, we know won't work
$arg = ['milk', 'cereal']; # Sets up something like "ARRAY(0x183f0d4)" in $arg
my @arg = ('milk', 'cereal'); # We could /also/ do this, but @arg is nothing to do with $arg, so it's fairly pointless
# (a list) (a scalar with a list reference)
} # And ends, with no external effect
my @thelist = ('eggs1', 'juice'); # List (@-prefixed)
my $thelist = ['eggs2', 'juice']; # /Reference/ to a list (scalar string, with "ARRAY(...)"-type thing contained)
my %thehash = ('eggs3'=>1, 'juice'=>1); # Hash (%-prefixed)
my $thehash = {'eggs4'=>1, 'juice'=>1}; # /Reference/ to a hash (scalar string, with "HASH(...)"-type thing contained)
SetY1a(\@thelist); # That'd work
SetY1a($thelist); # and this
SetY1h(\%thehash); # and this
SetY1h($thehash); # this too
print join(", ",@thelist)."\n"; # Prints the list @thelist
print join(", ",@$thelist)."\n"; # Prints the (separate) list referenced by $thelist
print join(", ",map {$_."=>".$thehash{$_}} keys %thehash)."\n"; # Prints the hash items from %thehash
print join(", ",map {$_."=>".$$thehash{$_}} keys %$thehash)."\n"; # Prints the (separate) hash items referenced by $thehash
setY2(@thelist); #
setY2($thelist); #
setY2(%thehash); #
setY2($thehash); # ...what can I say?
print join(", ",@thelist)."\n"; # Prints the list @thelist
print join(", ",@$thelist)."\n"; # Prints the (separate) list referenced by $thelist
print join(", ",map {$_."=>".$thehash{$_}} keys %thehash)."\n"; # Prints the hash items from %thehash
print join(", ",map {$_."=>".$$thehash{$_}} keys %$thehash)."\n"; # Prints the (separate) hash items referenced by $thehash
eggs1, spam # @thelist, after being sent as a reference
eggs2, spam # $thelist, already a reference
eggs3=>1, spam=>1 # %thehash, after being sent as a reference
spam=>1, eggs4=>1 # $thehash, already a reference
eggs1, spam # @thelist was sent as a list ($arg='eggs1', but overwritten)
eggs2, spam # $thelist was sent as a scalar ($arg=an ARRAY() reference, but overwritten)
eggs3=>1, spam=>1 # %thehash was sent as a list ($arg='eggs3', or maybe 'spam', but overwritten)
spam=>1, eggs4=>1 # $thehash was sent as a scalar ($arg=a HASH() reference, but overwritten)
By the above equivalence, I see that Python is passing a reference (where Perl passes a value, and we have to provide the
value of the reference), and and then being used in a reference-friendly context (which is the default with object methods, like ".append" and ".remove"). When you use it in a non-referential context (as I also do in Perl, with setY2) then the passed reference is lost
>>> def setZ(obj, arg):
obj.val = arg
>>> setZ.val = "One"
>>> val2 = "Two"
>>> setZ(setZ, val2)
>>> setZ.val
'Two'
Ah, now, we're going into OO country... which I'd do
completely different in Perl, but in an attempt to keep it approximate to your example, I supposed I'd have to work with hashes, again, and do
something like...
sub setZ { my $self = shift; # First element of @_ is the reference object, extract it
if (@_) { $$self{'$val'} = shift } # Make the referenced item equal what comes after
# (after quickly testing that anything /does/ come after!)
} # end of setZ
# More down-to-earth Perlish version, lacking the OO '$self'-ishness.
sub setZZ { my ($ref,$val) = @_; # Following this?
$$ref{'val'}=$val; # Should work...
} # end of setZZ
my %setZ = (val=>"One"); my %setZZ = (val=>"One");
my $val2 = "Two"; my $val22 = "TwoTwo";
setZ(\%setZ,$val2); setZZ(\%setZZ,$val22);
print "SetZ: ".$setZ{'val'}."\tSetZZ: ".$setZZ{'val'}."\n";
SetZ: One SetZZ: TwoTwo
Assigning var1 to var2 passes the reference of var1 to var2:>>> var1 = "Apple"
>>> var2 = "Orange"
>>> var2 = var1
>>> var1, var2
('Apple', 'Apple')
>>> var1 = "Chocolate"
>>> var1, var2
('Chocolate', 'Apple')
You know (unless I've misread it disasterously) This looks like the same as copying values, with the assignment, as much as passing the references.
In fact, if var2 was point at var1 (when both became 'Apple'), then you should have ended up with ('Chocolate','Chocolate'). What you have there is values, I'm
fairly sure. Of course, your explanation (which is doubtless equally as correct) is that var2 points
exactly at what var1 was pointing at, but that now var1 is pointing somewhere different. But that's functionally no different from var1 always pointing at location A (first containts Apple, then Chocolate by the end), while var2 points at location B (first of all Orange, then the value from location A is copied into B, i.e. Apple, and this stays until the end, even after the location A has been changed).
## Natural value-based variable handling
my ($var1,$var2); # Just pre-defining these, to be nice to Perl if it's in full strict/warnings mode
# and to avoid having to "my" (sort of like "local", but not) them on the (next) first sight
# (because I want the rest of the assignments to look identical, above all)
$var1 = "Apple"; # $var1 is "Apple". As literally as memory abstractions allow.
$var2 = "Orange"; # $var2 is "Orange".
$var2 = $var1; # $var2 is given a literal value of "Apple"
print "A1: $var1, $var2\n"; # What the $vars contain
print "A0: ".\$var1.", ".\$var2."\n"; # Where the $vars reside
$var1 = "Chocolate"; # $var1 is given a literal value of "Chocolate", which doesn't affect $var2;
print "B1: $var1, $var2\n"; # What the $vars contain
print "B0: ".\$var1.", ".\$var2."\n"; # Where the $vars reside
## Now let's do it with references
my ($Apple,$Orange,$Chocolate)=("Apple", "Orange", "Chocolate"); # ( Yes, I could have qw/quoted/ that list, for those that know Perl. )
print "Apple='$Apple'\@".\$Apple.", Orange='$Orange'\@".\$Orange.", Chocolate='$Chocolate'\@".\$Chocolate."\n"; # The original breakdown
$var1 = \$Apple; # $var1 is now a reference to where "Apple" is stored,
$var2 = \$Orange; # $var2 is now a reference to where "Orange" is stored,
$var2 = $var1; # $var2 is now a reference identical to that of $var1;
print "C1: $var1, $var2\n"; # What the $vars contain (raw)
print "C2: $$var1, $$var2\n"; # What the $vars ultimately contain (following their references)
$$var1 = "Apple2"; # A small diversion from the original example
print "C1: $var1, $var2\n"; # What the $vars contain (raw)
print "C2: $$var1, $$var2\n"; # What the $vars ultimately contain (following their references)
$var1 = \$Chocolate; # $var1 is now re-referenced to where "Chocolate" is stored,
print "D1: $var1, $var2\n"; # What the $vars contain (raw)
print "D2: $$var1, $$var2\n"; # What the $vars ultimately contain (following their references)
print "Apple='$Apple'\@".\$Apple.", Orange='$Orange'\@".\$Orange.", Chocolate='$Chocolate'\@".\$Chocolate."\n"; # The final breakdown
A1: Apple, Apple # So we set $var2's value to $var1's value
A0: SCALAR(0x1829b2c), SCALAR(0x1829b4c) # But $var1 and $var2 are in different locations
B1: Chocolate, Apple # We've reset $var1
B0: SCALAR(0x1829b2c), SCALAR(0x1829b4c) # And $var1 and $var2 are in the /same/ different locations
Apple='Apple'@SCALAR(0x1845f2c), Orange='Orange'@SCALAR(0x1845f0c), Chocolate='Chocolate'@SCALAR(0x1845eec)
# Contents of $Apple is 'Apple' at SCALAR(...), etc;
C1: SCALAR(0x1845f2c), SCALAR(0x1845f2c) # This time we're using references, so var2 is pointing to the same as var1
C2: Apple, Apple # And they're the same because they're the same end
C1: SCALAR(0x1845f2c), SCALAR(0x1845f2c) # Also provable by changing what $var1 pointed at (and $var2 also does)
C2: Apple2, Apple2 # ...and giving them the same alteration
D1: SCALAR(0x1845eec), SCALAR(0x1845f2c) # Now we point $var1 at somewhere new
D2: Chocolate, Apple2 #
Apple='Apple2'@SCALAR(0x1845f2c), Orange='Orange'@SCALAR(0x1845f0c), Chocolate='Chocolate'@SCALAR(0x1845eec)
# Contents of $Apple is 'Apple2' at (same) SCALAR(...), etc;
>>> var1 = ['apple', 'orange', 'banana']
>>> var2 = var1
>>> var1.remove('banana')
>>> var1, var2
(['apple', 'orange'], ['apple', 'orange'])
This also works well in Perl, when explicitly using the
referencing form of variables.
my ($var1h, $var1a, $var2h, $var2a); # Just setting these up (uninitialised), again
$var1h = {apple=>1, orange=>1, banana=>1}; # Here using a hash structure, because it's easier to ".remove" an item.
$var1a = ['apple', 'orange', 'banana']; # This is the array version, and I'll use something less specific on it
print "Var1h: $var1h;\tVar1a: $var1a;\n"; # Just to show that references are held.
print "H1: ".join(", ",map {$_."=>".$$var1h{$_}} keys %$var1h)."\n"; # Prints the hash items referenced by $var1h
print "A1: ".join(", ",@$var1a)."\n"; # Prints the list referenced by $var1a
$var2h = $var1h;
$var2a = $var1a;
print "Var2h: $var2h;\tVar2a: $var2a;\n"; # Just to show the references that are held.
print "H2: ".join(", ",map {$_."=>".$$var2h{$_}} keys %$var2h)."\n"; # Prints the hash items referenced by $var2h
print "A2: ".join(", ",@$var2a)."\n"; # Prints the list referenced by $var2a
delete $$var1h{banana}; # Remove the 'banana' item explicitly from the referenced hash
pop @$var1a; # Remove the last item (happens to be 'banana') from the array
print "Var1h: $var1h;\tVar1a: $var1a;\n"; # Just to show the references that are held.
print "Var2h: $var2h;\tVar2a: $var2a;\n"; # Just to show the references that are held.
print "H1: ".join(", ",map {$_."=>".$$var1h{$_}} keys %$var1h)."\n"; # Prints the hash items referenced by $var1h
print "A1: ".join(", ",@$var1a)."\n"; # Prints the list referenced by $var1a
print "H2: ".join(", ",map {$_."=>".$$var2h{$_}} keys %$var2h)."\n"; # Prints the hash items referenced by $var2h
print "A2: ".join(", ",@$var2a)."\n"; # Prints the list referenced by $var2a
Var1h: HASH(0x239b94); Var1a: ARRAY(0x239cd4); # One hash and one array set up, as references
H1: banana=>1, apple=>1, orange=>1 # Here's what's in the referenced hash (order is indeterminate)
A1: apple, orange, banana # Here's what's in the referenced array (order maintained)
Var2h: HASH(0x239b94); Var2a: ARRAY(0x239cd4); # New variables copy references from old ones
H2: banana=>1, apple=>1, orange=>1 # Hash contents, again
A2: apple, orange, banana # Array contents, again
Var1h: HASH(0x239b94); Var1a: ARRAY(0x239cd4); # After 'banana' has been removed, from both structures
Var2h: HASH(0x239b94); Var2a: ARRAY(0x239cd4); # ...and of course everything's still pointing at the same places
H1: apple=>1, orange=>1 # Yes, we have no bananas,
A1: apple, orange # We have no bananas today,
# We've broad beans like bunions, cabbbahges and honions
# And all kinds of fruit and say
# We' have an old fashioned tomahto, a nice Jersey potahto
H2: apple=>1, orange=>1 # But yes, we have no bananas,
A2: apple, orange # We have no bananas today!
(With apologies for the Music-hall lyrics.
)