sort procedure,
that is, assigning a boolean-valued binary function and passing it to sort.
The second method that Robert provided is ingenious. Here it is, in full,
Ls:= map(convert,L,bytes): Ls:= sort(Ls, lexorder): map(convert,Ls,bytes);
It converts each list into a string, sorts the strings, then converts the strings back to lists. This method is significantly faster than the previous. It does, however, have a limitation; it can only operate on lists with positive integers in the range 1..255. While that limitation was suitable for the original poster's application, that will not always be the case.
It would be convenient to be able to shift the range of allowable consecutive integers, say to include 0. That can be readily accomplished by adding/subtracting before and after sorting, however, doing so increases the time and memory usage. There is a method to avoid some of the cost, that is, to use sorting with attributes. Here is an example showing how that can be done:
sortlex := proc(L, inc::integer:=1)
local lst;
description
"sort lexically a list of lists of integers; "
"the integers must be in the range 1-inc .. 255-inc";
map(attributes, sort([seq(setattribute(convert(map(`+`,lst,inc)
,'bytes'),lst)
, lst=L)]));
end proc:
For example,
L := RandomTools:-Generate(listlist(integer(range=-5..5), 4, 3));
L := [[-5, -5, -3], [-1, -1, 4], [4, 4, 5], [-4, 2, -2]]
sortlex(L,6);
[[-5, -5, -3], [-4, 2, -2], [-1, -1, 4], [4, 4, 5]]
Because this procedure can fail if the integers are outside the allowable range,
it is prudent to ensure that this is case. This can be accomplished using
the depends parameter modifier that was introduced in Maple 10.
sortlex := proc(L::depends(list(list(integer[1-inc..255-inc])))
, inc::integer:=1)
local lst;
description
"sort lexically a list of lists of integers; "
"the integers must be in the range 1-inc .. 255-inc"
map(attributes, sort([seq(setattribute(convert(map(`+`,lst,inc)
,'bytes'), lst)
, lst=L)]));
end proc:
For my amusement I extended Robert's technique so that it can be used with integers in the range
1-inc .. 2552-1-inc. It is more efficient than list-based sorting, at least for a sufficient number of lists. Here is the code.
sortlex8 := proc(L::depends(list(list(integer[1-inc..255^2-1-inc])))
, inc::integer:=1)
local lst,r;
map(attributes, sort([seq(setattribute(convert(
map(i -> (iquo(i+inc,255,'r')+1,r+1), lst)
, 'bytes' ), lst)
, lst=L)]));
end proc:
Comments
Very interesting, but ...
Update: The 'but...'-part of this post is no longer relevant as Joe Riel has now broken the lines that were too long to be properly displayed.
Very interesting reading, but, unfortunately, the longer code lines of yours are hidden behind the right column of "Recent comments", etc., if viewed in a resolution of 1024x768 (I guess that it is me being a little "altmodisch", technologically speaking).
A general solution
I have obtained the following general solution, I believe, to the problem of sorting a list of lists (of equal lengths) of arbitrary integers:
improved
That is a clever technique. Your sortLists procedure can be improved. Here is faster version
Alas, that isn't as fast as using the standard element by element comparison, at least not for
typical lists. Besides, the element-by-element comparison is more general in that it works with lists of differing sizes.
Quite right
Thanks for your improved procedure. I wrote my procedure the way I did because I thought that
or in your case
which is ill-defined for L1 = L2 would cause problems if evaluated. But I suppose the reason why your procedure works is that Maple does not need to evaluate this expression if L1 = L2 evaluates true because the boolean operator is an 'or'. Indeed, interchanging the two boolean expressions does produce the error "invalid subscript selector".
on the right track
Yes. Note that the same occurs using the `if` function, that is, `if` uses special evaluation rules, so only the needed parameter is evaluated. You could have done
Actually, there is a subtle flaw in that, which your procedure shares. The result returned is not of the type truefalse, instead it may be a relation, say, 1 < 0. Apparently sort is able to deal with that, so it isn't really a flaw, but I think it is better to call evalb, that is,
Using the or returns truefalse without the evalb
evalb()
Actually, I did consider using evalb() for the very same reason that you describe. But as it worked without I thought I would just stick to the shorter version.
You see, in the field of programming I am a self-taught person having no formal education, so unlike you and the other Maple masters, I suppose, some of the finer issues of programming, like evaluation rules, are often quite novel to me.
sorting with attributes
If your list is large, I suggest sorting with attributes. You will need a bound B on the absolute value of the largest element:
The method is fast because it avoids computing an expensive function n*log(n) times. Despite the use of floating point numbers, you should never have issues with precision because the value X.L[i] is computed exactly and a float is constructed with full precision..
comments
There is a minor problem with your encoding scheme, it doesn't work with the mix of positive and negative integers implied by the assignment to r, the random number generator.
Note that in the range of 1..255, Robert's method is substantially faster, especially as the sublists increase in length.
It works fine
...it doesn't work with the mix of positive and negative integers implied by the assignment to r...
I think it works fine. What problem do you see ?
for small values of fine
It's not bijective on the domain.
Actually, since attributes are used, it only has to be injective (and preserve ordering). That can be fixed, but will slow down the computation.
Oops, should use 2*B
I can't believe I didn't notice that - thanks. One cheap way to fix it is to use 2*B+1, ie: 2001.