The overall structure is a list assignment similar in concept to:
my ($a, $b) = (1, 2);
The right side of the assignment uses the match to generate a list of two values to be assigned to the variables of the left side. Here is how to think about the match:
$0 =~ m#(path stuff)/(program name stuff)$#
The program name part of the pattern is critical. With the end-of-line anchor it says "match all non-slash character from the end of the line to the last slash." The $ is what makes it "think backwards." The path name part is just there to suck up everything before the program name part. The slash that separates them is discarded.
This code has a serious bug - it fails if there is no path.
Here is your code with a sample path (instead of $0) and some print statements. Also, ");" is needed at the end of your statement to make it work:
my $in = "/usr/home/wruppert/perl/myprog.pl";
my ($d_home, $f_self) = ($in =~ m#(.+)/([^/]+)$#);
print $d_home, "\n";
print $f_self, "\n";
/usr/home/wruppert/perl
myprog.pl
Of course, there are modules already written to do this correctly and in an OS independent fashion.
use File::Basename;
my ($name, $path) = fileparse($in);
print $path, "\n";
print $name, "\n";
/usr/home/wruppert/perl/
myprog.pl