Perl6: Unary Sort(perl6advent.wordpress.com) |
Perl6: Unary Sort(perl6advent.wordpress.com) |
Personally, I like the freedom that Perl offers. Perl 5.x is still widely used in some industries, finance and banking for instance, and it's still the glue that holds 'nix systems together (have a look at the scripts in the bin folders of any distro).
Following Modern Perl[1] best practices, you can write powerful, meaningful and expressive Perl 5 without shooting yourself in the foot.
Perl 6 is another beast, it removes many of the ambiguities present in Perl 5 and introduces more functional paradigms.
At its core, Perl remains a multipurpose tool. The fact that there are multiple ways to do a thing is not bad, whether it suits you or not is a matter of personal preference really.
So, if someone is curious about Perl, they should be encouraged to try it and find out for themselves if they like it or not.
A nice language will offer you enough creative ammunition and will do as you wish while you are busy at more important things.
["A","b","C"].sort {|a, b| a.downcase <=> b.downcase }
to ["A","b","C"].sort_by {|k| k.downcase }
(or equivalently) ["A","b","C"].sort_by &:downcaseBut sometimes you need BOTH a key extraction closure (to get key caching for performance) AND a comparison closure (to specify a custom sort). In P6 you just specify both closures (P6 figures out which is which because one has one arg and the other has two).
Here's the P6 equivalent of your last couple lines:
["A","b","C"].sort: { .lc } # A, b, C
["A","b","C"].sort: *.lc # same thing
Here's a custom sort: ["A","b","C"].sort: { $^b.lc leg $^a.lc } # C, b, A
Now combining them: ["A","b","C"].sort: *.lc, { $^b leg $^a } # C, b, A
The latter will run faster.> Note that in Perl 6, cmp is smart enough to compare strings with string semantics and numbers with number semantics, so producing numbers in the transformation code generally does what you want.
Perl 5 had a clear distinction between "cmp" (string comparison) and "<=>" (numeric comparison). Trying to work out the data type, and thus the comparison approach, from the actual data itself, is surely going to create subtle bugs, that don't appear in testing, but do appear with live data.
list.sort(key=lambda x: x.lower()) key=str.lower
It will be faster, but it will break on elements that aren't strings (which could be good or bad). key=operator.methodcaller("lower")
and handle any type.python can sort according to a function (using the "key" parameter) or using a custom comparator function (using the "cmp" parameter)
as a side note, Python offers also a sorted[0] function, applyable to any sequence
All I'm saying is that the author bills it as not wanting to run { .lc } twice per comparison. What I'm adding is that the comparison function itself, even if trivial, arguably has a bit of an overhead just from calling it. Thus, having O(n) calls to a key function { .lc } may be better than O(n log n) calls to a comparison function {.lc <=> .lc}.
At least that's how people convinced me to use key= instead of cmp= in Python.
http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec...
Rakudo/MoarVM began running this month. As Larry Wall said a few days ago "failed 179 test files ... which ain't too shabby at this point" http://irclog.perlgeek.de/moarvm/2013-12-21#i_8029074
Afaik the Perl 6 book is not really being updated. Maybe this info is of interest to you: http://www.perlmonks.org/?node_id=1033899
Sorry HNers and P6ers.
I feel so embarrassed. I'm usually almost pathologically careful. I have an explanation if anyone wants it but I suspect an excuse is uninteresting so will skip that for now.
Instead I'll say I am now committed to trying to implement this (it's in the spec, just not yet implemented) by updating Rakudo's sort method:
https://github.com/rakudo/rakudo/blob/874e358fa5b09f94243003...
The good news is that it's (currently) about 10 lines of code. The bad news is they look very gutsy to me. Oh well. Open mouth, take responsibility, do your best and all that.
Likewise, if you produce a number, you have an object which does the Numeric role, and it will compare as a number.
mylist.sort(key=lambda element: element.attribute)
Elsewere, say you want to sort a non-omogeneous list, or according to a custom order (attribute1 descending, attribute2 ascending) cmp make this easyYou can do this in python with the key function returning a list/tuple:
mylist.sort(key=lambda element: (element.attribute1, -element.attribute2)) my $a = [2,1]; say $a.sort; say $a # 1 2 2 1
my $a = [2,1]; say $a.=sort; say $a # 1 2 1 2
This riffs off the general op= syntax: my $a = 1; say $a + 1; say $a # 2 1
my $a = 1; say $a += 1; say $a # 2 2Lisp had a nice idiom that Randal Schwartz translated in to Perl 5 in 1994 which became known as the Schwartzian Transform: http://en.wikipedia.org/wiki/Schwartzian_Transform
It seems from the comments here that Python's key and cmp args are Python's equivalent to the ST. I'm surprised by the comment that Python 3 removed cmp. Perhaps that's a temporary omission.
This HN is about an advent article for Perl 6, which covers the same territory. Based on a look at the Rakudo code, it only implements a single custom sort closure (equivalent to Python's cmp) OR a single key closure (equivalent to Python's key).
Guido and company insist that you can (almost?) always find a way supplying a key function. For better or worse, they decided it was the best way, and going by the Python philosophy they removed anything that isn't the one obvious right way to do it. Perhaps it's available in a library somehow, that's what they did to other things that are unnecessary or useful 99% of the time but may still be 1% of the time.
Similarly, Guido once almost removed lambda, but he faced an insurrection.
def cmp_to_key(mycmp):
"""Convert a cmp= function into a key= function"""
class K(object):
__slots__ = ['obj']
def __init__(self, obj, *args):
self.obj = obj
def __lt__(self, other):
return mycmp(self.obj, other.obj) < 0
def __gt__(self, other):
return mycmp(self.obj, other.obj) > 0
def __eq__(self, other):
return mycmp(self.obj, other.obj) == 0
def __le__(self, other):
return mycmp(self.obj, other.obj) <= 0
def __ge__(self, other):
return mycmp(self.obj, other.obj) >= 0
def __ne__(self, other):
return mycmp(self.obj, other.obj) != 0
__hash__ = None
return K