Jump to content

Category talk:Wren-sort: Difference between revisions

m
→‎Source code: Now uses Wren S/H lexer.
(Copied over source code from previous 'sort' module talk page.)
 
m (→‎Source code: Now uses Wren S/H lexer.)
 
(14 intermediate revisions by the same user not shown)
Line 1:
===Source code===
 
<langsyntaxhighlight ecmascriptlang="wren">/* Module "sort.wren" */
import "./trait" for Comparable
 
/*
Line 23 ⟶ 24:
return Fn.new { |s1, s2|
if (s1 == s2) return 0
var cp1 = s1.codePoints.toList
var cp2 = s2.codePoints.toList
var len = (cp1.count <= cp2.count) ? cp1.count : cp2.count
for (i in 0...len) {
Line 76 ⟶ 77:
Sort contains various sorting methods which may be useful in different scenarios.
As it would be too expensive to check that all elements of a large list are of the
same type or compatible types, errors are left to emerge naturally. In place sorting
methods return the original list after sorting.
*/
class Sort {
Line 86 ⟶ 88:
isList_(a)
var c = a.count
if (c < 2 || s < 0 || s >= c || e <= s || e >= c) return a
if (!e) e = c - 1
if (cmp == true) cmp = Cmp.defaultDesc(a[0])
if (!cmp) cmp = Cmp.default(a[0])
quick_(a, s, e, cmp)
return a
}
 
Line 110 ⟶ 113:
}
}
System.write("") // fixes VM recursion bug
quick_(a, s, r, cmp)
quick_(a, l, e, cmp)
Line 157 ⟶ 159:
isList_(a)
var count = a.count
if (count < 2) return a
if (cmp == true) cmp = Cmp.defaultDesc(a[0])
if (!cmp) cmp = Cmp.default(a[0])
Line 173 ⟶ 175:
siftDown_(a, 0, end, cmp)
}
return a
}
 
Line 196 ⟶ 199:
isList_(a)
var c = a.count
if (c < 2) return a
if (cmp == true) cmp = Cmp.defaultDesc(a[0])
if (!cmp) cmp = Cmp.default(a[0])
Line 208 ⟶ 211:
a[j+1] = v
}
return a
}
 
Line 214 ⟶ 218:
isList_(a)
var c = a.count
if (c < 2) return a
if (cmp == true) cmp = Cmp.defaultDesc(a[0])
if (!cmp) cmp = Cmp.default(a[0])
Line 229 ⟶ 233:
}
}
return a
}
 
Line 235 ⟶ 240:
isList_(a)
var n = a.count
if (n < 2) return a
if (cmp == true) cmp = Cmp.defaultDesc(a[0])
if (!cmp) cmp = Cmp.default(a[0])
Line 252 ⟶ 257:
}
}
return a
}
 
Line 292 ⟶ 298:
static isSorted(a) { isSorted(a, false) }
static isSortedDesc(a) { isSorted(a, true) }
 
// Reverses a list in place.
static reverse(a) {
var c = a.count
if (c < 2) return
var i = 0
var j = a.count - 1
while (i < j) {
var t = a[i]
a[i] = a[j]
a[j] = t
i = i + 1
j = j - 1
}
}
}
 
Line 363 ⟶ 354:
return (t[1] > 0) ? t[2].to : -1
}
 
// Works similarly to 'all' but only returns the index of the first match
// or the index at which it would need to be inserted if there were no matches at all.
static nearest(a, value, cmp) { all(a, value, cmp)[2].from }
 
// Finds the lowest value in an unsorted list according to 'cmp' but without sorting.
Line 410 ⟶ 405:
}
return [max, iMax.count, iMax]
}
 
// Private helper function for 'quick' method.
static partition_(a, left, right, pivotIndex, cmp) {
var pivotValue = a[pivotIndex]
a[pivotIndex] = a[right]
a[right] = pivotValue
var storeIndex = left
var i = left
while (i < right) {
if (cmp.call(a[i], pivotValue) < 0) {
var t = a[storeIndex]
a[storeIndex] = a[i]
a[i] = t
storeIndex = storeIndex + 1
}
i = i + 1
}
var temp = a[right]
a[right] = a[storeIndex]
a[storeIndex] = temp
return storeIndex
}
 
// Finds the 'k'th smallest element of an unsorted list according to 'cmp'
// using the 'quickselect' algorithm. 'k' is zero based.
static quick(a, k, cmp) {
Sort.isList_(a)
if (k.type != Num || !k.isInteger || k < 0) {
Fiber.abort("'k' must be a non-negative integer")
}
var count = a.count
if (count <= k) Fiber.abort("The list is too small.")
if (count == 1) return a[0]
if (cmp == true) cmp = Cmp.defaultDesc(a[0])
if (!cmp) cmp = Cmp.default(a[0])
var left = 0
var right = count - 1
while (true) {
if (left == right) return a[left]
var pivotIndex = ((left + right)/2).floor
pivotIndex = partition_(a, left, right, pivotIndex, cmp)
if (k == pivotIndex) {
return a[k]
} else if (k < pivotIndex) {
right = pivotIndex - 1
} else {
left = pivotIndex + 1
}
}
}
 
// Convenience versions of the above which use default values for the 'cmp' parameter.
static all(a, value) { all(a, value, false) }
static first(a, value) { first(a, value, false) }
static last(a, value) { last(a, value, false) }
static lowestnearest(a, value) { lowestnearest(a, value, false) }
static highestlowest(a) { highestlowest(a, false) }
static highest(a) { highest(a, false) }
static quick(a, k) { quick(a, k, false) }
 
// Finds the median element(s) of a sorted list.
Line 434 ⟶ 481:
 
/*
SortedList represents a list which is always sorted with respect to some comparison function.
Comparable is an abstract class which enables child classes to automatically
This means that lookups are much faster for large lists as we can use binary search.
inherit the comparison operators by just overriding the 'compare' method.
When the entire list needs to be sorted (as opposed to a single element 'insertion' sort) and
*/
has at least 10 elements, the 'quicksort' algorithm is used and so the sort is unstable.
class Comparable {
If a stable sort is required, add items one by one, never in bulk.
compare(other) {
For methods which require an index to be passed, negative indices are supported.
// This should be overridden in child classes to return -1, 0 or +1
*/
// depending on whether this < other, this == other or this > other.
class SortedList is Sequence {
// Private helper function to check that 'a' is a sequence and throw an error otherwise.
static isSeq_(a) { (a is Sequence) ? true : Fiber.abort("Argument must be a sequence.") }
 
// Private helper function to check that 'cmp' is a suitable comparison function
// and throw an error otherwise.
static isCmp_(cmp) {
if ((cmp is Fn) && cmp.arity == 2) return true
Fiber.abort("Argument must be a comparison function which takes 2 arguments.")
}
 
// Private helper method to sort 'lst' in place using 'cmp' and a suitable algorithm.
< (other) { compare(other) < 0 }
static sortAll_(lst, cmp) {
> (other) { compare(other) > 0 }
<=(other) { compare if (other)lst.count <= 010) }{
Sort.insertion(lst, cmp)
>=(other) { compare(other) >= 0 }
==(other) { compare(other) == 0 } else {
Sort.quick(lst, 0, lst.count - 1, cmp)
!=(other) { compare(other) != 0 }
}
}
}
 
// Creates a new empty SortedList using a comparer 'cmp'.
construct new(cmp) {
SortedList.isCmp_(cmp)
_lst = []
_cmp = cmp
}
 
// Private constructor to create a new empty SortedList
// from a list a which is already sorted using 'cmp'.
construct new_(a, cmp) {
_lst = a
_cmp = cmp
}
 
// Creates a new SortedList from a sequence 'a' and comparer 'cmp'.
construct fromSeq(a, cmp) {
SortedList.isSeq_(a)
SortedList.isCmp_(cmp)
_lst = a.toList
_cmp = cmp
SortedList.sortAll_(_lst, _cmp)
}
 
// Creates a new SortedList from a sequence 'a'
// using the default comparer for its first element's type.
construct fromSeq(a) {
SortedList.isSeq_(a)
if (a.count == 0) {
Fiber.abort("Sequence must have at least one element to deduce the comparer.")
}
_lst = a.toList
if ((a is List) || (a is SortedList)) {
_cmp = Cmp.default(_lst[0])
} else {
_cmp = Cmp.default(_lst.take(1).toList[0])
}
SortedList.sortAll_(_lst, _cmp)
}
 
// Creates a new SortedList from a single value 's'
// using a comparer 'cmp'.
construct fromOne(s, cmp) {
SortedList.isCmp_(cmp)
_lst = [s]
_cmp = cmp
}
 
// Creates a new SortedList from a single value 's'
// using the default comparer for its type.
construct fromOne(s) {
_lst = [s]
_cmp = Cmp.default(s)
}
 
// Returns the number of elements in the sorted list.
count { _lst.count }
 
// Returns whether or not the sorted list is empty.
isEmpty { count == 0 }
 
// Removes all elements from the sorted list.
clear() { _lst.clear() }
 
// Adds 'item' to the sorted list and returns it.
add(item) {
_lst.add(item)
Sort.insertion(_lst, _cmp)
return item
}
 
// Adds each element of a sequence 'other' to the sorted list
// and returns the added items.
addAll(other) {
SortedList.isSeq_(other)
_lst.addAll(other)
SortedList.sortAll_(_lst, _cmp)
return other
}
 
// Removes the first element in the sorted list that matches 'e'.
// and returns it or returns null if not found.
remove(e) {
var ix = indexOf(e)
if (ix == -1) return null
return _lst.removeAt(ix)
}
 
// Removes all elements in the sorted list that match 'e'
// Returns 'value' if there were any removals, otherwise 'null'.
removeAll(e) {
var ix = indexOf(e)
if (ix == -1) return null
while (ix < _lst.count && _lst[ix] == e) _lst.removeAt(ix)
return e
}
 
// Removes the element at index 'i' and returns it.
removeAt(i) { _lst.removeAt(i) }
 
// Removes all duplicates from 'this' and returns the result.
prune() {
var c = _lst.count
if (c < 2) return this
for (i in c-1..1) {
if (_lst[i-1] == _lst[i]) _lst.removeAt(i)
}
return this
}
 
// Returns a new Sorted list consisting only of distinct elements of 'this'.
distinct {
var sl = copy()
sl.prune()
return sl
}
 
// Replaces all occurrences of 'old' by 'swap' in the sorted list and returns ['old', 'swap'].
replace(old, swap) {
var ix = indexOf(old)
if (ix >= 0) {
while (ix < _lst.count && _lst[ix] == old) {
_lst[ix] = swap
ix = ix + 1
}
SortedList.sortAll_(_lst, _cmp)
}
return [old, swap]
}
 
// Returns a copy of this.
copy() { SortedList.new_(_lst.toList, _cmp) }
 
// Converts 'this' to an 'ordinary' list and returns the result.
toList { _lst.toList }
 
// Returns the internal list for use with List methods which don't mutate it.
// If they do, re-sort 'this' afterwards.
asList { _lst }
 
// Resort 'this' using a (different) comparison function and returns the result.
resort(cmp) {
SortedList.isCmp_(cmp)
_cmp = cmp
SortedList.sortAll_(_lst, _cmp)
return this
}
 
// Resort 'this' using the current comparison function and returns the result.
// Not usually needed though see 'asList'.
resort() {
SortedList.sortAll_(_lst, _cmp)
return this
}
 
// Returns the element (or a copy of the range of elements)
// at index 'i' within the sorted list.
[index] {
if (!(index is Range)) {
return _lst[index]
}
return SortedList.new_(_lst[index], _cmp)
}
 
// Replaces the element at 'index' in the sorted list with 'item'.
[index]=(item) {
_lst[index] = item
Sort.insertion(_lst, _cmp)
}
 
// Uses binary search to find the index of the first occurence of 'value'
// in the sorted list. Returns -1 if not found.
indexOf(value) { Find.first(_lst, value, _cmp) }
 
// Uses binary search to find the index of the last occurence of 'value'
// in the sorted list. Returns -1 if not found.
lastIndexOf(value) { Find.last(_lst, value, _cmp) }
 
// Uses binary search to find the index of the first occurence of 'value'
// in the sorted list. Returns the index at which it would need to be inserted
// if not found.
nearestIndexOf(value) { Find.nearest(_lst, value, _cmp) }
 
// Iterator protocol methods.
iterate(iterator) { _lst.iterate(iterator) }
iteratorValue(iterator) { _lst.iteratorValue(iterator) }
 
// Creates a new sorted list by adding the elements of a sequence 'other'
// to the elements of 'this' and using the same comparer to sort them.
+(other) {
var res = _lst + other
SortedList.sortAll_(res, _cmp)
return SortedList.new_(res, _cmp)
}
 
// Creates a new sorted list by repeating the present one 'count' times
// and then sorting it.
*(count) {
var res = _lst * count
SortedList.sortAll_(res, _cmp)
return SortedList.new_(res, _cmp)
}
 
// Returns the string representation of 'this'.
// Type aliases for classes in case of any name clashes with other modules.
toString { _lst.toString }
var Cmp_Cmp = Cmp
}</syntaxhighlight>
var Cmp_Sort = Sort
var Cmp_Find = Find
var Cmp_Comparable = Comparable</lang>
9,476

edits

Cookies help us deliver our services. By using our services, you agree to our use of cookies.