Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

Prototype like sort()?

by perlancar (Hermit)
on Jan 25, 2018 at 08:53 UTC ( [id://1207882]=perlquestion: print w/replies, xml ) Need Help??

perlancar has asked for the wisdom of the Perl Monks concerning the following question:

Would it be possible to declare a function that can be called like the built-in sort(), particularly:

myfunc 1, 2, 3, 4;
myfunc { $a->[0] cmp $b->[0] } 1, 2, 3, 4;

The Prototypes section in perlsub suggests that this is not possible.

Replies are listed 'Best First'.
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:

    1. sort SUBNAME LIST
    2. sort CODEREF LIST
    3. 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";
Re: Prototype like sort()?
by johngg (Canon) on Jan 25, 2018 at 12:02 UTC

    I'm not sure what you are after. Do you mean something like this?

    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.



Re: Prototype like sort()?
by ikegami (Patriarch) on Jan 26, 2018 at 01:45 UTC

    The syntax of sort can't be replicated by subs.

    >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 { ... }, ...)
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

      That doesn't allow mysort() to accept coderef without the "sub" like sort() does:

      mysort {$b<=>$a} 4,2,1,3; # syntax error
        Ok, fair enough. I am beginning to understand your question, but I'm not there yet. $a and $b are special variables that Perl uses.

        This can't be simply about sort. Perhaps, can you explain more specifically about what you want?

      "...a whole bunch of reasons to not use prototypes..."

      Yes, may be. But some cool modules do it all the time - e.g.:

      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

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 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).

      Yup, not quite. sort can be called without a block, while using the & prototype means your sub has to be called with a block. Perhaps if Perl supported a prototype like [&@$] like it does \[&@$].

        Yes, right, this syntax allows only the block syntax, not the expression syntax. And I don't think there is any simple way to enable an expression syntax for such a sort subroutine (or for a custom clone of the map or grep functions).

        But that does not deprive you of any functionality: any sort construct using the sort expression syntax can be easily transformed into a block syntax, essentially by adding a pair of curly brackets and removing the comma.

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 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.

      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!

        Unfortunately there is an ugly and hard to debug edge case when using an &call with an empty list.

        From perlsub

        &foo;        # foo() get current args, like foo(@_) !!

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Wikisyntax for the Monastery


        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. 

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Wikisyntax for the Monastery

        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 :)

      Others explained that not all builtins' APIs can be copied by using prototypes.

      But if you are willing to combine your rank with another modifier sub like by with prototype, you could form compound statements like

      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.

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)
      Wikisyntax for the Monastery

        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.
Re: Prototype like sort()?
by Anonymous Monk on Jan 25, 2018 at 11:55 UTC
    sub mysort(&@){...} Is as good as it gets

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1207882]
Front-paged by haukex
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (6)
As of 2024-06-14 14:50 GMT
Find Nodes?
    Voting Booth?

    No recent polls found

    erzuuli‥ 🛈The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.