Re: Prototype like sort()?
by pryrt (Abbot) on Jan 25, 2018 at 14:42 UTC
|
perlancar, it appears many fellow monks are having trouble understanding your requirements. johngg++ and marshall++ had reasonable interpretations, but not the same I had. I will describe my interpretation below, but (unfortunately) I don't have an exact solution if my interpretation is right, though I come close to what I believe your requirements are. Please let us know if this is what you want. If not, you will have to give more detail.
The builtin sort has three alternate syntaxes:
- sort SUBNAME LIST
- sort CODEREF LIST
- sort LIST
From your quoted code example, I infer that you want to be able to mimic syntaxes #2 and #3 (since your example doesn't show #1, I will assume you don't want it), but have some different functionality. Thus, I think you are asking if there is a subroutine prototype that will make the first CODEREF argument optional, but still without having to have the prefix sub to initiate the anonymous subroutine, and without having to have the comma between the closing } of the CODEREF and the start of the LIST.
By using something like the following, I can come close to the syntaxes listed, but not exactly replicate them:
use warnings;
use strict;
sub myfunc(@) {
my $default_subfunc = sub {
local $"=',';
return "my default code block: (@_)\n";
};
my $subfunc = ( 'CODE' eq ref $_[0] ) ? shift : $default_subfunc;
return $subfunc->(@_);
}
#3: no CODEREF
print myfunc 1, 2, 3;
#2a: two-line version, with comma:
my $alternate = sub { "alt: sizeof LIST = " . scalar(@_) . "\n" };
print myfunc $alternate, 1..10;
#2b: one-line version, with comma:
print myfunc sub { local $"=';'; return "oneliner: @_\n" }, qw/hello w
+orld/;
#2fail: one-line version, no comma, no sub:
eval qq| # this eval-wrapper prevents the compiler from crashing, so
+the previous examples will still run
print myfunc { local $"=';'; return "oneliner: @_\n" } qw/hello world/
+;
|;
if($@) {
my $x = $@;
$x =~ s/^/\t/gm;
print "ERROR:{\n$x\n}\n";
}
# I assume you already knew the & prototype, to mimic _just_ #2's synt
+ax
sub otherfunc(&@) {
my $subfunc = shift;
return $subfunc->(@_);
}
print otherfunc { "otherfunc always needs a coderef: @_\n" } 5..7;
print otherfunc sub { $alternate->(@_) }, 2,3,5,7,11;
print "END\n";
| [reply] [d/l] [select] |
Re: Prototype like sort()?
by johngg (Canon) on Jan 25, 2018 at 12:02 UTC
|
use strict;
use warnings;
use 5.022;
use List::Util qw{ sum };
sub groupsOf (&$@);
say for groupsOf { sum @_ } 3, 1 .. 20;
sub groupsOf (&$@)
{
my $rcToRun = shift;
my $groupsOf = shift;
my $rcDoIt;
$rcDoIt = sub
{
$rcToRun->(
map shift, 1 .. ( @_ < $groupsOf ? @_ : $groupsOf )
),
@_ ? &$rcDoIt : ();
};
&$rcDoIt;
}
The output.
6
15
24
33
42
51
39
If this guess is wrong please clarify your requirement.
| [reply] [d/l] [select] |
Re: Prototype like sort()?
by ikegami (Patriarch) on Jan 26, 2018 at 01:45 UTC
|
>perl -E"say defined(prototype('CORE::sort')) ? 'can replicate' : 'can
+\'t replicate'"
can't replicate
The best you can do is &@.
sub mysort(&@) {
my $code_ref = shift;
...
}
mysort { ... } ... # Same as: mysort(sub { ... }, ...)
| [reply] [d/l] [select] |
Re: Prototype like sort()?
by Marshall (Canon) on Jan 25, 2018 at 09:43 UTC
|
There are a whole bunch of reasons to not use prototypes. But a simple example of something that I don't recommend...
#!/usr/bin/perl
use warnings;
use strict;
sub mysort (@)
{
my @array = @_;
return sort @array;
}
my @abc = mysort 4,2,1,3;
print "@abc"; #prints 1 2 3 4
| [reply] [d/l] |
|
mysort {$b<=>$a} 4,2,1,3; # syntax error | [reply] |
|
| [reply] |
|
|
|
sub apply (&@)
{
my $action = shift;
&$action foreach my @values = @_;
wantarray ? @values : $values[-1];
}
Best regards, Karl
«The Crux of the Biscuit is the Apostrophe»
perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help
| [reply] [d/l] [select] |
Re: Prototype like sort()?
by Laurent_R (Canon) on Jan 27, 2018 at 22:58 UTC
|
FWIW, this is an implementation in Perl of an exotic sort algorithm, comb sort (or Dobosiewicz's sort - see https://en.wikipedia.org/wiki/Comb_sort for details), using prototypes to mimic Perl's internal sort function:
sub comb_sort (&\@) {
my $code_ref = shift;
my $v_ref = shift;
my $max = scalar (@$v_ref);
my $gap = $max;
while (1) {
my $swapped = 0;
$gap = int ($gap / 1.3);
$gap = 1 if $gap < 1;
my $lmax = $max - $gap - 1;
foreach my $i (0..$lmax) {
local ($a, $b) = ($$v_ref[$i], $$v_ref[$i+$gap]);
($$v_ref[$i], $$v_ref[$i+$gap], $swapped) = ($$v_ref[$i+$g
+ap], $$v_ref[$i], 1)
if $code_ref->($a, $b) > 0;
}
last if $gap == 1 and $swapped == 0;
}
}
This sort subroutine can be called with a code block just as Perl's internal sort. For example:
#!/usr/bin/perl
use strict;
use warnings;
my @v;
my $max = 500;
$v[$_] = int rand(20000) foreach (0..$max);
comb_sort {$a<=>$b} @v;
print "@v";
I'm not sure that's what you're after, but you can see above a calling syntax with a simple code block (no need to build an actual subroutine).
| [reply] [d/l] [select] |
|
| [reply] [d/l] [select] |
|
| [reply] [d/l] [select] |
Re: Prototype like sort()?
by perlancar (Hermit) on Jan 27, 2018 at 01:17 UTC
|
Thanks for all the answers, and sorry for not explaining myself more clearly. Basically I want to provide a sort-like function that users can use like sort(). The actual code I'm talking about is List::Rank.
sort SUBNAME LIST
sort BLOCK LIST
sort LIST
If users don't specify BLOCK or SUBNAME then sorting will be done the default way (lexically). If BLOCK or SUBNAME is specified, then sorting will be done using that custom sorter.
Seeing that this is not possible, I settled with separate subroutines. | [reply] |
|
I'm a bit late to the party, I just wanted to point out that it's not impossible to have one sub do it all, just ugly (and with the limitation that in the third form, the first thing in the list can't be a coderef):
sub mysort (&@) {
my $sub = ref $_[0] eq 'CODE' ? shift : sub {...};
...
}
sub subname {...};
mysort {...} 'x', 'y', 'z';
mysort \&subname, 'x', 'y', 'z';
&mysort( 'x', 'y', 'z');
On the other hand, I like your solution of rank @list vs. rankby {...} @list! | [reply] [d/l] [select] |
|
| [reply] [d/l] |
|
Nice!
to elaborate further, the trick you are using is that prepending & to a sub let's the parser ignore the prototype.
> perlsub: Not only does the & form make the argument list optional, it also disables any prototype checking on arguments you do provide. This is partly for historical reasons, and partly for having a convenient way to cheat if you know what you're doing.
| [reply] |
|
Yup, all the time I write subs that behave differently depending on the type of arguments. The most common is:
foo([ \%opts ], $arg, ...)
and then trying to detect the optional hashref in the first argument.
What I'm looking for here is the syntax bonus :)
| [reply] [d/l] |
|
rank LIST;
rank by BLOCK LIST;
rank by {SUBNAME} LIST;
I find this more readable than your original approach.
If acceptable, I could elaborate further on a clean implementation.
| [reply] [d/l] [select] |
|
That's nice, I got reminded of TOBYINK's PerlX::Maybe when reading that code. But it's too "magical" for my taste. Thanks for the offer though.
| [reply] |
|
|
|
Re: Prototype like sort()?
by Anonymous Monk on Jan 25, 2018 at 11:55 UTC
|
sub mysort(&@){...} Is as good as it gets | [reply] |