Up to date timesort to resolve list issues.

This commit is contained in:
D3athrow
2015-02-05 17:44:48 -06:00
parent fe3866f28c
commit e3746f7547
16 changed files with 169 additions and 246 deletions

View File

@@ -31,29 +31,28 @@
return "[output][and_text][input[index]]"
//Returns list element or null. Should prevent "index out of bounds" error.
proc/listgetindex(var/list/list,index)
if(istype(list) && list.len)
/proc/listgetindex(list/L, index)
if(istype(L))
if(isnum(index))
if(IsInRange(index,1,list.len))
return list[index]
else if(index in list)
return list[index]
if(IsInRange(index,1,L.len))
return L[index]
else if(index in L)
return L[index]
return
proc/islist(list/list)
if(istype(list))
/proc/islist(list/L)
if(istype(L))
return 1
return 0
//Return either pick(list) or null if list is not of type /list or is empty
proc/safepick(list/list)
if(!islist(list) || !list.len)
return
return pick(list)
/proc/safepick(list/L)
if(istype(L) && L.len)
return pick(L)
//Checks if the list is empty
proc/isemptylist(list/list)
if(!list.len)
/proc/isemptylist(list/L)
if(!L.len)
return 1
return 0
@@ -65,17 +64,20 @@ proc/isemptylist(list/list)
return 0
//Empties the list by setting the length to 0. Hopefully the elements get garbage collected
proc/clearlist(list/list)
/proc/clearlist(list/list)
if(istype(list))
list.len = 0
return
//Removes any null entries from the list
proc/listclearnulls(list/list)
if(istype(list))
while(null in list)
list -= null
return
/proc/listclearnulls(list/L)
if(istype(L))
var/i=1
for(var/thing in L)
if(thing != null)
++i
continue
L.Cut(i,i+1)
/*
* Returns list containing all the entries from first list that are not present in second.
@@ -127,21 +129,43 @@ proc/listclearnulls(list/list)
return null
//Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/listfrom)
if (listfrom.len > 0)
var/picked = pick(listfrom)
listfrom -= picked
return picked
return null
/proc/pick_n_take(list/L)
if(L.len)
var/picked = rand(1,L.len)
. = L[picked]
L.Cut(picked,picked+1) //Cut is far more efficient that Remove()
//Returns the top(last) element from the list and removes it from the list (typical stack function)
/proc/pop(list/listfrom)
if (listfrom.len > 0)
var/picked = listfrom[listfrom.len]
listfrom.len--
return picked
return null
/proc/pop(list/L)
if(L.len)
. = L[L.len]
L.len--
/proc/sorted_insert(list/L, thing, comparator)
var/pos = L.len
while(pos > 0 && call(comparator)(thing, L[pos]) > 0)
pos--
L.Insert(pos+1, thing)
// Returns the next item in a list
/proc/next_list_item(var/item, var/list/L)
var/i
i = L.Find(item)
if(i == L.len)
i = 1
else
i++
return L[i]
// Returns the previous item in a list
/proc/previous_list_item(var/item, var/list/L)
var/i
i = L.Find(item)
if(i == 1)
i = L.len
else
i--
return L[i]
/*
* Sorting
@@ -165,6 +189,7 @@ proc/listclearnulls(list/list)
L.Swap(i,rand(1,L.len))
return L
//Return a list with no duplicate entries
/proc/uniquelist(var/list/L)
var/list/K = list()
@@ -184,39 +209,12 @@ proc/listclearnulls(list/list)
//any value in a list
/proc/sortList(var/list/L, cmp=/proc/cmp_text_asc)
if(!istype(L))
return
return sortTim(L.Copy(), cmp)
//uses sortList() but uses the var's name specifically. This should probably be using mergeAtom() instead
/proc/sortNames(var/list/L, order=1)
return sortTim(L, order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc)
//Mergesort: divides up the list into halves to begin the sort
/proc/sortAtom(var/list/atom/L, var/order = 1)
if(isnull(L) || L.len < 2)
return L
var/middle = L.len / 2 + 1
return mergeAtoms(sortAtom(L.Copy(0,middle)), sortAtom(L.Copy(middle)), order)
//Mergsort: does the actual sorting and returns the results back to sortAtom
/proc/mergeAtoms(var/list/atom/L, var/list/atom/R, var/order = 1)
var/Li=1
var/Ri=1
ASSERT(L)
ASSERT(R)
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
var/atom/rL = L[Li]
var/atom/rR = R[Ri]
if(sorttext(rL.name, rR.name) == order)
result += L[Li++]
else
result += R[Ri++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
//Converts a bitfield to a list of numbers (or words if a wordlist is provided)
/proc/bitfield2list(bitfield = 0, list/wordlist)
@@ -251,104 +249,38 @@ proc/listclearnulls(list/list)
i++
return i
//Don't use this on lists larger than half a dozen or so
/proc/insertion_sort_numeric_list_ascending(var/list/L)
//world.log << "ascending len input: [L.len]"
var/list/out = list(pop(L))
for(var/entry in L)
if(isnum(entry))
var/success = 0
for(var/i=1, i<=out.len, i++)
if(entry <= out[i])
success = 1
out.Insert(i, entry)
break
if(!success)
out.Add(entry)
//world.log << " output: [out.len]"
return out
/proc/insertion_sort_numeric_list_descending(var/list/L)
//world.log << "descending len input: [L.len]"
var/list/out = insertion_sort_numeric_list_ascending(L)
//world.log << " output: [out.len]"
return reverseRange(out)
// List of lists, sorts by element[key] - for things like crew monitoring computer sorting records by name.
/proc/sortByKey(var/list/L, var/key)
if(L.len < 2)
return L
var/middle = L.len / 2 + 1
return mergeKeyedLists(sortByKey(L.Copy(0, middle), key), sortByKey(L.Copy(middle), key), key)
/proc/mergeKeyedLists(var/list/L, var/list/R, var/key)
var/Li=1
var/Ri=1
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
if(sorttext(L[Li][key], R[Ri][key]) < 1)
// Works around list += list2 merging lists; it's not pretty but it works
result += "temp item"
result[result.len] = R[Ri++]
else
result += "temp item"
result[result.len] = L[Li++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
//Mergesort: any value in a list, preserves key=value structure
/proc/sortAssoc(var/list/L)
if(L.len < 2)
return L
var/middle = L.len / 2 + 1 // Copy is first,second-1
return mergeAssoc(sortAssoc(L.Copy(0,middle)), sortAssoc(L.Copy(middle))) //second parameter null = to end of list
/proc/mergeAssoc(var/list/L, var/list/R)
var/Li=1
var/Ri=1
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
if(sorttext(L[Li], R[Ri]) < 1)
result += R&R[Ri++]
else
result += L&L[Li++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
/proc/find_record(field, value, list/L)
for(var/datum/data/record/R in L)
if(R.fields[field] == value)
return R
//Move a single element from position fromIndex within a list, to position toIndex
//All elements in the range [1,toIndex) before the move will be before the pivot afterwards
//All elements in the range [toIndex, L.len+1) before the move will be after the pivot afterwards
//In other words, it's as if the range [fromIndex,toIndex) have been rotated using a <<< operation common to other languages.
//fromIndex and toIndex must be in the range [1,L.len+1]
//This will preserve associations ~Carnie
/proc/moveElement(list/L, fromIndex, toIndex)
if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
return
if(fromIndex > toIndex)
++fromIndex
else
++toIndex
++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
L.Insert(toIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(fromIndex, fromIndex+1)
//Move elements [fromIndex,fromIndex+len) to [toIndex,toIndex+len)
//This will preserve associations and is much faster than copying to a new list
//or checking for associative lists and manually copying elements ~Carnie
//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex)
//Same as moveElement but for ranges of elements
//This will preserve associations ~Carnie
/proc/moveRange(list/L, fromIndex, toIndex, len=1)
var/distance = abs(toIndex - fromIndex)
if(len > distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going
if(fromIndex < toIndex)
toIndex += len
else
fromIndex += len
if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
if(fromIndex <= toIndex)
return //no need to move
fromIndex += len //we want to shift left instead of right
for(var/i=0, i<distance, ++i)
L.Insert(fromIndex, null)
@@ -357,8 +289,6 @@ proc/listclearnulls(list/list)
else
if(fromIndex > toIndex)
fromIndex += len
else
toIndex += len //?
for(var/i=0, i<len, ++i)
L.Insert(toIndex, null)
@@ -403,4 +333,4 @@ proc/listclearnulls(list/list)
while(start < end)
L.Swap(start++,end--)
return L
return L