Re: Aren't there code refs as well as function refs?
by hippo (Archbishop) on Mar 03, 2023 at 23:37 UTC
|
What you get if you put { $a = 1 } somewhere, as opposed to the function ref you get from sub { $a = 1 }?
What you get is an anonymous block - it isn't a reference. It is used primarily (by me, anyway) to limit scope. What you call "function refs" are what I would call code refs.
Not sure if this is useful to you or not. Perhaps if you could provide a more fully-fledged example it might help?
| [reply] |
|
|
It may well be useful; if it is useful, it is as evidence towards the hypothesis that things don't work the way I think they do.
There are a lot of places where anonymous blocks occur in magic being worked by packages. Here's an example from the perldoc for Test::Exception where the throws_ok function uses this:
throws_ok { read_file( 'unreadable' ) } qr/No file/, 'no file';
I'm wondering what throws_ok() receives, where, and how. Yes, I notice the missing comma; this is like a print statement where the first optional param (fd in that case) is not separated by a comma from the regular arguments.
| [reply] [d/l] |
|
|
| [reply] [d/l] |
|
|
The prototype of throw_ok is &$;$, so
throws_ok { ... } ..., ...;
is equivalent to
&throws_ok( sub { ... }, scalar( ... ), scalar( ... ) );
| [reply] [d/l] [select] |
|
|
What you call "function refs" are what I would call code refs.
I would like to point out a distinction. In a function ref, you cannot just simply modify a variable of the caller except if it is included in the argument list. In a code ref, you could modify the caller's variables but the only way to do this is with the eval() function. Is there any other way to do it? (In the following code, the PrintPattern() function modifies its first argument. But a nicer solution would be to be able to directly modify the caller function's variable.)
#!/usr/bin/perl
use strict;
use warnings;
my $TRIANGLE =
'..Z2.X6.V343.T2132312.RI.P4E4.N8A8.L3636363.J313231323132313.Hc..';
DrawPattern1($TRIANGLE);
DrawPattern2($TRIANGLE);
DrawPattern3($TRIANGLE);
#####################################################################
# This function converts the input pattern from plain text to binary.
# Also returns the length of the pattern and a couple of zeros.
# Usage: ($PATTERN, $LENGTH, 0, 0) = GetPattern($TEXT)
#
sub GetPattern
{
my $PATTERN = defined $_[0] ? $_[0] : '';
# Remove everything except letters and numbers and periods.
$PATTERN =~ tr|.0-9A-Za-z||cd;
# Replace all numbers and letters with characters 00 - 3E
$PATTERN =~ tr|.0-9A-Za-z|\x40\x00-\x3E|;
return ($PATTERN, length($PATTERN), 0, 0);
}
#####################################################################
# This function prints a pattern to stdout.
# The pattern should be plain text. Each even numbered letter
# specifies the number of spaces to print, and odd numbered
# letters specify the number of '#' signs to print.
# A period creates a line break.
# Usage: DrawPattern1(STRING)
#
sub DrawPattern1
{
my ($PATTERN, $LENGTH, $MODE, $c) = GetPattern($_[0]);
for (my $i = 0; $i < $LENGTH; $i++)
{
$c = ord(substr($PATTERN, $i, 1));
PrintPattern($MODE, $c);
}
}
#####################################################################
# Prints a number of spaces or pound signs to stdout, or a new line.
# Changes the value of the first argument.
# Usage: PrintPattern(WHAT_TO_PRINT, COUNT)
#
sub PrintPattern
{
@_ == 2 or return;
my ($MODE, $COUNT) = @_;
$COUNT < 64 or return $_[0] = print "\n";
$_[0] = $MODE = $MODE & 1 ? 32 : 35;
print chr($MODE) x $COUNT;
}
#####################################################################
# This function does exactly the same thing as DrawPattern1()
# Usage: DrawPattern2(STRING)
#
sub DrawPattern2
{
my ($PATTERN, $LENGTH, $MODE, $c) = GetPattern($_[0]);
for (my $i = 0; $i < $LENGTH; $i++)
{
$c = ord(substr($PATTERN, $i, 1));
if ($c == 64) { $MODE = print "\n"; next; }
print chr($MODE = $MODE & 1 ? 32 : 35) x $c;
}
}
#####################################################################
# This function does exactly the same thing as DrawPattern1()
# Usage: DrawPattern3(STRING)
#
sub DrawPattern3
{
my ($PATTERN, $LENGTH, $MODE, $c) = GetPattern($_[0]);
# This is nice but won't work:
my $code_ref = \"if (\$c == 64) { \$MODE = print \"\\n\"; next; }"
. " print chr(\$MODE = \$MODE & 1 ? 32 : 35) x \$c; ";
# This will work:
my $CODE_REF = \"if (\$c == 64) { \$MODE = print \"\\n\"; }" .
" else { print chr(\$MODE = \$MODE & 1 ? 32 : 35) x \$c; } ";
for (my $i = 0; $i < $LENGTH; $i++)
{
$c = ord(substr($PATTERN, $i, 1));
eval($$CODE_REF);
# This is, of course, not a very good idea, because
# repeatedly using eval() slows down the program.
}
}
#####################################################################
| [reply] [d/l] |
|
|
G'day harangzsolt33,
"I would like to point out a distinction."
You have failed to do that.
The 108 lines of code that you've posted contain no references to functions, subroutines, or any other code.
Calling a variable $CODE_REF does not make it a coderef.
$CODE_REF is not any kind of reference; it's just a string:
$ perl -MO=Deparse -e 'my $CODE_REF = \"if (\$c == 64) { \$MODE = prin
+t \"\\n\"; }" . " else { print chr(\$MODE = \$MODE & 1 ? 32 : 35) x \
+$c; } ";'
my $CODE_REF = 'SCALAR(0xa00038be0) else { print chr($MODE = $MODE & 1
+ ? 32 : 35) x $c; } ';
-e syntax OK
You can explicitly check it yourself:
$ perl -e 'my $CODE_REF = \"if (\$c == 64) { \$MODE = print \"\\n\"; }
+" . " else { print chr(\$MODE = \$MODE & 1 ? 32 : 35) x \$c; } "; my
+$ref = ref $code_ref; print $ref ? $ref : "not a ref";'
not a ref
Or let Perl do it for you:
$ perl -Mstrict -e 'my $CODE_REF = \"if (\$c == 64) { \$MODE = print \
+"\\n\"; }" . " else { print chr(\$MODE = \$MODE & 1 ? 32 : 35) x \$c;
+ } "; $CODE_REF->();'
Can't use string ("SCALAR(0xa00038c70) else { print"...) as a subrouti
+ne ref while "strict refs" in use at -e line 1.
"Is there any other way to do it?"
You can modify a caller's variables by passing references to those variables.
Here's a very simple example:
#!/usr/bin/env perl
use strict;
use warnings;
my ($x, $y) = qw{X Y};
print "BEFORE:\n";
print "\$x[$x] \$y[$y]\n";
mod_callers_vars(\$x, \$y);
print "AFTER:\n";
print "\$x[$x] \$y[$y]\n";
sub mod_callers_vars {
my ($x_ref, $y_ref) = @_;
$$x_ref = 'A';
$$y_ref = 'B';
return;
}
Output:
BEFORE:
$x[X] $y[Y]
AFTER:
$x[A] $y[B]
I see LanX has shown you how to use closures (which could be appropriate for more complex scenarios).
In "Re^4: Aren't there code refs as well as function refs?", you wrote:
"... you decide to call each function with 30 or so arguments."
That would be a very poor decision. Instead, use a single hashref argument.
| [reply] [d/l] [select] |
|
|
|
|
| [reply] |
|
|
|
|
|
|
|
Can't use string ("SCALAR(0x26afcc8) else { print c"...) as a SCALAR r
+ef while "strict refs" in use at garbage.pl line 100.
| [reply] [d/l] |
Re: Aren't there code refs as well as function refs?
by LanX (Saint) on Mar 04, 2023 at 07:04 UTC
|
That's a very confusing question: code-ref and function-ref are used synonymously.
This a ref of a named function
This a ref of a anonymous function
This is a code-block and not (necessarily) in a function, they are important for scooping and as syntactic element like for if, map or sub (sic)
Not to be confused with an anonymous hash ref
(Code-blocks and anonymous hash references are distinguished by "looks-like guessing")
Curlies are also used for explicitly marking symbol names after sigils
- "X${name}X" same as "X" . $name ."X"
A special syntactic sugar comes with are prototype (&)
for arguments. One can skip the sub when passing an anonymous code-ref
After defining
you can write
instead of
And calling bar will fail at compile time if anything else than a code ref is passed.
THAT'S ALL!
This should be a list of all uses of curly brackets in
Perl (omitting sublanguage syntax like regex)
Please explain what you don't understand.
| [reply] [d/l] [select] |
|
|
my $string = join q{ }, q(Hello), q/world/;
Hash keys:
my %hash;
$hash{foo} = 42;
@hash{ 'bar', 'baz' } = ( 666, 999 );
Unicode:
say "\N{DROMEDARY CAMEL}";
say "\x{1F42A}";
| [reply] [d/l] [select] |
|
|
| [reply] [d/l] [select] |
|
|
|
|
Re: Aren't there code refs as well as function refs?
by kcott (Archbishop) on Mar 04, 2023 at 03:05 UTC
|
G'day dd-b,
I think one of the main issues you may be having here is terminology.
At the end of the second paragraph of "perlsub: DESCRIPTION" (my emphasis):
"Often a function without an explicit return statement is called a subroutine, but there's really no difference from Perl's perspective."
You're making a distinction between "function refs" and "code refs": they're really just the same thing.
$ perl -E '
use Scalar::Util "reftype";
sub routine { 1; }
my $x = \&routine;
say $x;
say ref $x;
say reftype $x;
say sub { 1; };
'
CODE(0xa0004a3c0)
CODE
CODE
CODE(0xa0007e370)
In your follow-up post, you made reference to this type of syntax (which I've abstracted):
function { code } args
Before reading on, you may want to review "perlsub: Prototypes"
(noting what the '&' means in a prototype)
and prototype().
Also be aware that subroutines/functions do not necessarily have a prototype.
You'll find that syntax in a number of builtin functions; e.g. grep,
map & sort.
There's a plethora of examples in the builtin module List::Util.
There are also many examples in CPAN modules;
such as Test::Exception which you've identified.
Here's some example prototypes:
$ perl -E 'use List::Util; say prototype "List::Util::reduce"'
&@
$ perl -E 'use Test::Exception; say prototype "Test::Exception::throws
+_ok"'
&$;$
There are two other places where you'll often see '{ ... }':
anonymous blocks (see ++hippo's reply)
and hashref constructors (with which I'll assume you're familiar).
| [reply] [d/l] [select] |
|
|
Prototypes kept going! I kind of went off them a long time ago, they seemed to not be where things were going. Also--I notice they don't affect indirect and other common kinds of calls; this would look hugely likely to cause immense confusion!
I can of course dig into the places where I wonder what's going on. That code tends to be over my head, though, so it's a deep dive.
Perl is always an adventure!
| [reply] |
|
|
I avoid prototypes in my code, except for the empty prototype '()' to create
"Constant Functions".
In v5.36.0, the experimental status of subroutine Signatures was removed.
You may find this to be a better method to declare parameters.
Do note that prototypes and signatures are not different syntaxes for the same thing: follow the link for details.
This is something I do like and have been using in all of my personal code since Perl v5.36.0 was released.
Not that I'm recommending it but, just so you know,
you can use both a prototype and a signature with the same subroutine.
If you do this, use of the :prototype() attribute
is advised to avoid ambiguity.
| [reply] [d/l] [select] |
|
|