Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

implicit sort disables a chained subroutine?

by BrowserUk (Patriarch)
on Jan 13, 2005 at 20:05 UTC ( [id://422083]=perlquestion: print w/replies, xml ) Need Help??

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

The bound to be a simple explanation for this phenomena, but if there is, I cannot see it?

I was trying to use my uniq() utility routine, which has seen service in it's present form for months, if not years, and it suddenly stopped working. It gave no visible errors or warnings, it simply failed to do anything.

After scratching my head most of last night, I gave up. Today, I realised that the key to the failure was the fact that I was

  • chaining it with sort.
  • and that I was using the implicit default comparator block.

    What am I doing wrong?

    #! perl -slw use strict; sub uniq { my %x; @x{@_} = (); keys %x } my @array = ( 'A' .. 'Z', reverse 'A' .. 'Z' ); ## Explicit sort block works as expected print sort { $a cmp $b } uniq @array; ## implicit sort block "disables"!? the uniq() sub? print sort uniq @array; __DATA__ P:\test>junk ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUTSRQPONMLKJIHGFEDCBA

    Examine what is said, not who speaks.
    Silence betokens consent.
    Love the truth but pardon error.
  • Replies are listed 'Best First'.
    Re: implicit sort disables a chained subroutine?
    by dragonchild (Archbishop) on Jan 13, 2005 at 20:12 UTC
      Ahh, but you're not using the default comparator block. sort has three prototypes:
      • sort USERSUB LIST
      • sort BLOCK LIST
      • sort LIST

      USERSUB, if given, is the name of a subroutine that returns an integer less than, equal to, or greater than 0, depending on how the elements of the list are to be ordered.

      You're going to have to add the BLOCK there because you'll never get the third prototype.

      Being right, does not endow the right to be rude; politeness costs nothing.
      Being unknowing, is not the same as being stupid.
      Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
      Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

    Re: implicit sort disables a chained subroutine?
    by demerphq (Chancellor) on Jan 13, 2005 at 20:20 UTC

      The sub is being used as a comparitor. See the documentation for sort SUBNAME LIST

      #! perl -slw use strict; $a=$b='unused'; sub uniq { print "unique($a,$b):",@_; my %x; @x{@_} = (); keys %x } my @array = ( 'A' .. 'Z', reverse 'A' .. 'Z' ); ## Explicit sort block works as expected print sort { $a cmp $b } uniq @array; ## uniq is used a comparator here print sort uniq @array; __DATA__ unique(unused,unused):ABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUTSRQPONMLKJIHGFE +DCBA ABCDEFGHIJKLMNOPQRSTUVWXYZ unique(A,B): unique(C,D): unique(E,F): <SNIP> unique(K,J): unique(I,H): unique(G,F): unique(E,D): unique(C,B): ABCDEFGHIJKLMNOPQRSTUVWXYZZYXWVUTSRQPONMLKJIHGFEDCBA

      You need to use the full ampersand and brackets to avoid it.

      #! perl -slw use strict; sub uniq { my %x; @x{@_} = (); keys %x } my @array = ( 'A' .. 'Z', reverse 'A' .. 'Z' ); ## Explicit sort block works as expected print sort { $a cmp $b } uniq @array; ## must use &sub(@args); here or uniq is used as a comparator. print sort &uniq(@array); __DATA__ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ
      ---
      demerphq

        Thanks. That's the explanation that clinches it for me.

        I guess the distinction between using a built-in that returns a list, in a chain:

        print sort split'', $string;

        which works, and using a user sub which produces a list, in the same place:

        print sort uniq @array;

        which doesn't, is just another anomoly caused by the DWIMery of builtins.


        Examine what is said, not who speaks.
        Silence betokens consent.
        Love the truth but pardon error.
    Re: implicit sort disables a chained subroutine?
    by etcshadow (Priest) on Jan 13, 2005 at 23:44 UTC
      The question has already been answered, but, for what it's worth, a really easy way to avoid this is a plus sign:
      [me]$ perl -le 'sub uniq {keys%{{map{$_,undef}@_}}} print sort +uniq 1 +,4,2,2' 124 [me]$
      Same technique is often useful to disambiguate other ambiguously-parsable constructs.
      ------------ :Wq Not an editor command: Wq

        Now that is interesting. Thanks, it's infinitely preferable to using & and brackets.

        I know and use the unary + trick for disambiguating, but I would never have thought to try that here. I would have been worried that applying unary + to a function returning a list might have resulted in a scalar context.

        Now my (mostly rhetorical) question is why does that allow the parser to recognise the function name as a sub returning a list, as opposed to a comparator function being passed?

        I suspect the answer may lie in the deparse output that shows the undisabiguated function name being passed as a string constant, despite that doing so requires the parser to see the token uniq as a bareword and silently convert it to a string constant, which it shouldn't have as strict is in force.

        I am convinced that there is a bug here. I think that sort should either take a bare block, or the address of a function, if the comparator is supplied.

        Or possibly a string variable or constant that is the name of the comparator function, though I'm not personally fond of that option.

        Under no circumstances, especially with strict in force, should it silently coerse a bareword function name into a string constant.


        Examine what is said, not who speaks.
        Silence betokens consent.
        Love the truth but pardon error.

          Why not?

          Or possibly a string variable or constant that is the name of the comparator function, though I'm not personally fond of that option.

          You know, that's what it currently does. And it wouldn't make any difference in your case whether the comparator function were passed to sort as a coderef.

          The problem is not that it's passing the function by name, it's how the parser decides whether an identifier at the given position is to be taken as a comparator function. I would have preferred if that required an ampersand and no parens; this would be analogous to a \&@ prototype (but of course the block is optional so sort still can't use a prototype) and would be unambiguous.

          However, removing this special case from the parser at this point would break a lot of code so you're out of luck — it's ampersand and parens or a plus, no other option.

          Makeshifts last the longest.

    Re: implicit sort disables a chained subroutine?
    by Limbic~Region (Chancellor) on Jan 13, 2005 at 20:09 UTC
      BrowserUk,
      Changing it to following generates the desired results - parsing error perhaps?
      print sort( uniq( @array ) );
      Checking perldoc -f sort, 'sort SUBNAME LIST' is valid syntax but not likely the one you wanted.

      Cheers - L~R

        Checking perldoc -f sortsort subname list is valid syntax but not likely what you wanted.

        I use it with buil-ins all the time:

        print sort split '', 'fred'; d e f r

        so I don't see why it shouldn't work with user subs?

        It's also the nature of the misfunction that concerns me. It is as if the intervening user sub wasn't there or was simply acting as a pass-thru.

        No errors, no warnings, just the entire function of the sub silently bypassed?


        Examine what is said, not who speaks.
        Silence betokens consent.
        Love the truth but pardon error.
          BrowserUk,
          It isn't silently being bypassed. Put a print statement in uniq() and see for yourself. As far as why built-ins are working, I hope the other responses answer that.

          Cheers - L~R

    Re: implicit sort disables a chained subroutine?
    by JediWizard (Deacon) on Jan 13, 2005 at 20:15 UTC

      running the above code with perl -MO=Deparse results in the following:

      print sort(uniq(@array)); print sort('uniq' @array);
      May the Force be with you
    Re: implicit sort disables a chained subroutine?
    by blazar (Canon) on Jan 14, 2005 at 16:24 UTC
      perldoc -f sort
          sort SUBNAME LIST
          sort BLOCK LIST
          sort LIST
      

    Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Domain Nodelet?
    Node Status?
    node history
    Node Type: perlquestion [id://422083]
    Approved by Limbic~Region
    Front-paged by demerphq
    help
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this?Last hourOther CB clients
    Other Users?
    Others examining the Monastery: (4)
    As of 2024-04-24 11:48 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      No recent polls found