""" """ namespace Cobra.Core extend System.Collections.IList def addRange(items as System.Collections.IEnumerable) for item in items, .add(item) def swap(i as int, j as int) require i >= -.count and i < .count j >= -.count and j < .count ensure old this[i] == this[j] old this[j] == this[i] .count == old.count body count = .count if i < 0, i += count if j < 0, j += count temp = this[i] this[i] = this[j] this[j] = temp namespace Cobra.Core extend IList def addRange(items as IEnumerable) for item in items, .add(item) def concated(other as IList?) as IList """ Returns a new list with the contents of this list and the other. Does not modify this list or the other. The returned list is the same class as the receiver. Assumes the receiving type has an initializer that takes a `capacity as int`. """ ensure result is not this and result is not other result.count == .count + if(other, other.count, 0) result.typeOf is .typeOf body newList = .clone if other, (newList to dynamic).addRange(other) return newList def clone as IList # CC: as same """ Returns a new list with the contents of this list. Does not clone the contents themselves. This is sometimes known as a "shallow copy". """ ensure result is not this result.count == .count result.typeOf is .typeOf body type = .typeOf newList = type(this) return newList def get(flexibleIndex as int) as T """ Return the item at flexibleIndex. If flexibleIndex < 0 return item at flexibleIndex+.count, i.e. offset from end of list. Therefore: .get(0) returns first item in list .get(-1) returns last item .get(-2) returns second to last item .get(-.count) returns first item """ require .count > 0 (flexibleIndex >= 0 and flexibleIndex < .count) _ or (flexibleIndex < 0 and flexibleIndex >= -.count) body if flexibleIndex < 0, flexibleIndex += .count return this[flexibleIndex] def get(flexibleIndex as int, default as T) as T """ Return item at flexibleIndex. If flexibleIndex < 0 return item at flexibleIndex+.count. Index values outside list index range return default. """ ensure .count == 0 implies result == default (flexibleIndex > .count or flexibleIndex < -.count) _ implies result == default body count = .count if flexibleIndex < 0, flexibleIndex += count if flexibleIndex >= 0 and flexibleIndex < count return this[flexibleIndex] else return default def last as T """ Returns the last element in the list. """ require .count > 0 return this[.count-1] # TODO: haven't tested generic methods in generic extensions yet # def indexed(sig keyFor(value as T) as TKey) as Dictionary def numberedDown as KeyValuePair* """ Returns a stream of pairs of (index, value) in reverse order from the end of the list down. Can be used like so: for i, value in someList.numberedDown ... """ for index in .count-1 : -1 : -1 yield KeyValuePair(index, this[index]) def random as T """ Return an item at a random postion in the list. """ require .count > 0 return .random(CobraCore.random) def random(r as Random) as T require .count > 0 return this[r.next % .count] def removeLast """ Remove the last item in the list. """ require .count > 0 ensure .count == old .count - 1 .removeAt(.count-1) def pop as T """ Pop value off end/top of List. """ require .count > 0 count = .count value = this[count-1] .removeAt(count-1) return value def reversed as List """ Return a copy of this list reversed. """ ensure result is not this result.count == .count body t = List(this) t.reverse return t def sorted as List """ Return a copy of this list sorted. """ ensure result is not this result.count == .count body t = List(this) t.sort return t def sorted(comparison as Comparison) as List ensure result is not this result.count == .count body # TODO: needs test case t = List(this) t.sort(comparison) return t def sorted(comparer as Comparer) as List ensure result is not this result.count == .count body # TODO: needs test cas t = List(this) t.sort(comparer) return t def swap(i as int, j as int) """ Swaps the elements at the given indexes which can be negative to index from the end of the list towards the front (-1 is last element, -2 is second to last, etc.). """ require i >= -.count and i < .count j >= -.count and j < .count ensure old this[i] == this[j] old this[j] == this[i] .count == old .count body count = .count if i < 0, i += count if j < 0, j += count temp = this[i] this[i] = this[j] this[j] = temp class TestIListExtensions shared def hasContracts as bool try _hasContracts catch RequireException return true return false def _hasContracts require false pass test hasContracts = .hasContracts empty = List() # concated t = [1, 2] u = [3, 4] c = t.concated(u) assert c is not t and c is not u assert c == [1, 2, 3, 4] # clone t = [1, 2] tc = t.clone assert tc is not t and tc == [1, 2] # get(i) t = [1, 2] assert t.get(0) == 1 and t.get(1) == 2 assert t.get(-1) == 2 and t.get(-2) == 1 if hasContracts expect RequireException, t.get(-3) expect RequireException, t.get(2) t.clear expect RequireException, t.get(0) expect RequireException, t.get(1) expect RequireException, t.get(-1) else expect ArgumentOutOfRangeException, t.get(-3) expect ArgumentOutOfRangeException, t.get(2) t.clear expect ArgumentOutOfRangeException, t.get(0) expect ArgumentOutOfRangeException, t.get(1) expect ArgumentOutOfRangeException, t.get(-1) # get(i, default) t = [1, 2] assert t.get(0, 5) == 1 and t.get(1, 5) == 2 assert t.get(-1, 5) == 2 and t.get(-2, 5) == 1 assert t.get(-3, 5) == 5 and t.get(2, 5) == 5 t.clear assert t.get(0, 5) == 5 and t.get(1, 5) == 5 and t.get(-1, 5) == 5 # last t = [1, 2] assert t.last == 2 t.clear if hasContracts expect RequireException, t.last else expect ArgumentOutOfRangeException, t.last # pop t = [0] t.pop t.add(10) assert t.count == 1 and t[0] == 10 t.add(11) assert t.count == 2 assert t[0] == 10 and t[1] == 11 t.add(12) assert t.count == 3 assert t[0] == 10 and t[1] == 11 and t[2] == 12 assert t.pop == 12 assert t.count == 2 and t[1] == 11 and t[0] == 10 t.add(21) assert t.count == 3 and t[2] == 21 and t[1] == 11 and t[0] == 10 assert t.pop == 21 assert t.pop == 11 assert t.count == 1 and t[0] == 10 assert t.pop == 10 and t.count ==0 /# to-do if hasContracts expect RequireException, t.pop else expect ArgumentOutOfRangeException, t.pop #/ # numbered t = [10, 20, 30] count = 0 for pair in t.numbered assert pair.key == count assert (pair.key == 0 and pair.value == 10) or _ (pair.key == 1 and pair.value == 20) or _ (pair.key == 2 and pair.value == 30) count += 1 assert count == 3 count = 0 for i, v in t.numbered assert i == count assert (i==0 and v==10) or (i==1 and v==20) or (i==2 and v==30) count += 1 assert count == 3 # numberedDown t = [10, 20, 30] count = 2 for pair in t.numberedDown assert pair.key == count assert (pair.key == 0 and pair.value == 10) or _ (pair.key == 1 and pair.value == 20) or _ (pair.key == 2 and pair.value == 30) count -= 1 assert count == -1 count = 2 for i, v in t.numberedDown assert i == count assert (i==0 and v==10) or (i==1 and v==20) or (i==2 and v==30) count -= 1 assert count == -1 # reversed t = [1, 2, 3] u = t.reversed assert t is not u and t.count == u.count assert t == [1, 2, 3] and u == [3, 2, 1] t = List() u = t.reversed assert t is not u and t.count == u.count assert t == empty and u == empty # sorted t = [3, 1, 2] u = t.sorted assert t is not u and t == [3, 1, 2] and u == [1, 2, 3] t = List() u = t.reversed assert t is not u and t == empty and u == empty # swap t = [1, 2] t.swap(0, 1) assert t == [2, 1]