Hi, Kel. Happy Easter! 
Thanks for posting your “Finder sort” code. It certainly seems to get the required results for cgrafx’s purposes, though it’s a little on the slow side.
The main trick in keeping a script’s running time to a minimum is to see that it carries out as few incidental actions — and produces as few incidental strings and lists — as possible. This is often achieved through the way that instructions are phrased. For instance, you could replace ‘number of items in my_list’ (ie. “identify all the items of class ‘item’ in my_list and then get the ‘number’ of the result”) with ‘number of my_list’. Better still, since ‘number of’ is far slower than either ‘length of’ or ‘count’, use ‘count my_list’. However, it won’t make much difference here as you only use ‘number of’ in the outer loops of your process.
One of the busiest points in your script is the SplitString() handler, which is called twice from FinderCompare(), which in turn is called from within a nested repeat. The speed of the script nearly doubles on my machines if I change ‘repeat with this_char in rest of (text items of the_text)’ to:
repeat with i from 2 to (count the_text)
set this_char to item i of the_text
… because the list of ‘text items’ (you mean ‘characters’) is not created and neither is its ‘rest’.
Actually, the script I posted a couple of days ago wasn’t the version I thought it was. It was the original from before my correspondence with Rick. Here’s a bottled version of our joint effort. It’s longer than my original but faster, and can handle an unreasonably large number of file names. Like the original, it sorts lists in place rather than returning sorted copies of them, but this can be changed by making the property declaration for listCopy ‘property listCopy : theList’s items’.
It’s basic approach is pretty gross, but benefits from “economies of scale”:
1. Coerce the input list to a single string, using an unlikely character as a delimiter.
2. Traverse this string, identifying the "numeric" and "non-numeric" sections. Assemble the identified sections in another list, padding the numeric ones with leading zeros in the process.
3. Coerce the assembly list to a single string, using "" as a delimiter.
4. Using the unlikely delimiter, get the text items of this string. The result is a list equivalent to the original input list, but with the numeric portions of the strings padded out to the same length.
5. Quicksort the doctored list, mimicking each move in the original list.
Assumptions:
1. All the items in the original list are strings.
2. Mixed numeric/non-numeric strings are unlikely to contain runs of more than 16 numeric characters. However, the script can easily be adapted to accommodate more. (It may be possible to get away with 10, which would make the sort faster.)
It occurred to me while looking at your code that my own effort might be better off with an insertion sort rather than a Quicksort. Since the input list is most likely to be provided either by ‘list folder’ or by the Finder, it’ll be nearly in the right order anyway - a situation where an insertion sort would be more efficient. But my tests only give the insertion sort a fraction of a second’s advantage here, compared with several seconds disadvantage when the input list is [i]not[/] nearly in order!
(* Sorts a list of strings, treating numerics numerically rather than lexicographically. *)
on FinderDisplaySort(theList)
script o
-- The input list -- or a copy of it
property listCopy : theList --'s items
-- A list for doctored strings that will sort in the required order
property doctoredList : {}
-- A temporary list for assembling parts of doctored strings
property partList : {}
-- A delimiter character that's unlikely to be in any string in the list
property unlikelyDelim : ASCII character 1
-- The numeric characters
property theDigits : "1234567890"
-- A string of "zero" characters. Use as many as required to pad numerics.
property zeros : "00000000000000000000"
-- Prepare a version of the list with doctored strings.
on getDoctoredList()
-- Process the input list 3900 items (or less) at a time.
-- (3900 is near the maximum number of text items that can be extracted in one go.)
set listLen to (count listCopy)
set listL to 1
repeat until listL > listLen
set listR to listL + 3899 -- 3900 items...
if listR > listLen then set listR to listLen -- ... or less at a time
-- Coerce a batch of items to a single string using the unlikely delimiter
set listStr to my ListToString(items listL thru listR of my listCopy, unlikelyDelim)
considering case
-- The X Finder sorts underscores as spaces: replace the one with the other here
if (listStr contains "_") then set listStr to my ListToString(StringToList(listStr, "_"), space)
-- If the string contains any numerics, go through and modify them
if (listStr contains "1") or (listStr contains "2") or (listStr contains "3") or (listStr contains "4") or (listStr contains "5") or (listStr contains "6") or (listStr contains "7") or (listStr contains "8") or (listStr contains "9") or (listStr contains "0") then
set my partList to {}
set l to 1
set numeric to (character l of listStr is in theDigits) -- true or false
repeat with r from 2 to (count listStr)
if (numeric) then
if (character r of listStr is not in theDigits) then
set end of my partList to my padNumeric(text l thru (r - 1) of listStr)
set l to r
set numeric to false
end if
else if (character r of listStr is in theDigits) then
set end of my partList to text l thru (r - 1) of listStr
set l to r
set numeric to true
end if
end repeat
if (numeric) then
set end of my partList to my padNumeric(text l thru r of listStr)
else
set end of my partList to text l thru r of listStr
end if
set listStr to ListToString(partList, "")
end if
end considering
-- Break up the string again using the unlikely delimiter
-- and concatenate the list result to doctoredList
set doctoredList to doctoredList & StringToList(listStr, unlikelyDelim)
-- Advance the left input-list pointer for the next batch of strings
set listL to listR + 1
end repeat
return doctoredList
end getDoctoredList
-- Prepend enough leading zeros to a numeric string to make it 16 digits wide.
-- 16 is a compromise: fewer digits sort more quickly; more digits are less likely.
-- Names of cache files should sort lexicographically.
on padNumeric(theString)
set pad to 16 - (count theString)
if pad > 0 then
return (text 1 thru pad of zeros) & theString
else
return theString
end if
end padNumeric
-- turn string into list
on StringToList(theString, theDelim)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to theDelim
set theStringList to every text item of theString
set AppleScript's text item delimiters to astid
return theStringList
end StringToList
-- turn list into string
on ListToString(theStringList, theDelim)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to theDelim
set theString to theStringList as string
set AppleScript's text item delimiters to astid
return theString
end ListToString
(* QuickSort by Arthur J Knapp *)
-- This variation is hardwired to sort listCopy by copying the moves required to sort doctoredList.
on qMimicSort(l, r) -- sorts in-place
-- Much thanks to both Serge Belleudy-d'Espinose and Victor Yee
-- for the script-referencing techniques that they helped to
-- refine.
--
set i to l
set j to r
set v to my doctoredList's item ((l + r) div 2)
repeat while (j > i)
repeat while (my doctoredList's item i < v)
set i to i + 1
end repeat
repeat while (my doctoredList's item j > v)
set j to j - 1
end repeat
if (i is not greater than j) then
tell my doctoredList's item i
set my doctoredList's item i to my doctoredList's item j
set my doctoredList's item j to it
end tell
tell my listCopy's item i
set my listCopy's item i to my listCopy's item j
set my listCopy's item j to it
end tell
set i to i + 1
set j to j - 1
end if
end repeat
if (l < j) then qMimicSort(l, j)
if (r > i) then qMimicSort(i, r)
end qMimicSort
end script
tell o
getDoctoredList()
qMimicSort(1, count its listCopy)
return its listCopy
end tell
end FinderDisplaySort
set FinderSortedNames to FinderDisplaySort(list folder (choose folder) without invisibles)