| 1 | class HowToUseLists |
|---|
| 2 | |
|---|
| 3 | def main |
|---|
| 4 | .basics |
|---|
| 5 | .looping |
|---|
| 6 | .literals |
|---|
| 7 | .stringConversion |
|---|
| 8 | .operators |
|---|
| 9 | .methods |
|---|
| 10 | |
|---|
| 11 | def basics |
|---|
| 12 | # a list literal starts with [ and ends with ] |
|---|
| 13 | # elements are separated by commas |
|---|
| 14 | ints = [1, 2, 3] |
|---|
| 15 | |
|---|
| 16 | # a list has a number of elements - the .count |
|---|
| 17 | assert ints.count == 3 |
|---|
| 18 | |
|---|
| 19 | # lists can be indexed with brackets, starting with 0 |
|---|
| 20 | assert ints[0] == 1 and ints[1] == 2 and ints[2] == 3 |
|---|
| 21 | |
|---|
| 22 | # .last gives the last element |
|---|
| 23 | assert ints.last == 3 |
|---|
| 24 | |
|---|
| 25 | # out bounds is an exception |
|---|
| 26 | expect ArgumentOutOfRangeException, print ints[5] |
|---|
| 27 | |
|---|
| 28 | # .get allows a default value to be returned if the index is out of bounds |
|---|
| 29 | assert ints.get(3, -1) == -1 |
|---|
| 30 | |
|---|
| 31 | # .get allows negative indexes where -1 is the last element |
|---|
| 32 | # -2 is the second to last |
|---|
| 33 | assert ints.get(-2) == 2 |
|---|
| 34 | |
|---|
| 35 | # a negative index can still be out of bounds |
|---|
| 36 | expect Exception, ints.get(-4) |
|---|
| 37 | assert ints.get(-4, -1) == -1 # ...but you can provide a default value |
|---|
| 38 | |
|---|
| 39 | # lists can be compared |
|---|
| 40 | assert ints == [1, 2, 3] |
|---|
| 41 | |
|---|
| 42 | def looping |
|---|
| 43 | names = ['foo', 'bar', 'baz'] |
|---|
| 44 | |
|---|
| 45 | # looping is easy |
|---|
| 46 | for name in names |
|---|
| 47 | assert name.length == 3 |
|---|
| 48 | |
|---|
| 49 | # loop through a sorted version |
|---|
| 50 | last = '' |
|---|
| 51 | for name in names.sorted |
|---|
| 52 | assert name > last |
|---|
| 53 | last = name |
|---|
| 54 | # the original list is unchanged |
|---|
| 55 | assert names == ['foo', 'bar', 'baz'] |
|---|
| 56 | |
|---|
| 57 | # loop through a reversed version |
|---|
| 58 | for name in names.reversed |
|---|
| 59 | assert name.length == 3 |
|---|
| 60 | # again the original list is unchanged |
|---|
| 61 | assert names == ['foo', 'bar', 'baz'] |
|---|
| 62 | |
|---|
| 63 | # get an index with each element |
|---|
| 64 | order = [] |
|---|
| 65 | for i, name in names.numbered |
|---|
| 66 | branch i |
|---|
| 67 | on 0, assert name == 'foo' |
|---|
| 68 | on 1, assert name == 'bar' |
|---|
| 69 | on 2, assert name == 'baz' |
|---|
| 70 | order.add(i) |
|---|
| 71 | assert order == [0, 1, 2] |
|---|
| 72 | |
|---|
| 73 | # get an index with each element, but in reverse |
|---|
| 74 | order = [] |
|---|
| 75 | for i, name in names.numberedDown |
|---|
| 76 | branch i |
|---|
| 77 | on 0, assert name == 'foo' |
|---|
| 78 | on 1, assert name == 'bar' |
|---|
| 79 | on 2, assert name == 'baz' |
|---|
| 80 | order.add(i) |
|---|
| 81 | assert order == [2, 1, 0] |
|---|
| 82 | |
|---|
| 83 | # loop through the indexes only because the list will be modified |
|---|
| 84 | for i in names.count |
|---|
| 85 | names[i] = names[i].toUpper |
|---|
| 86 | assert names == ['FOO', 'BAR', 'BAZ'] |
|---|
| 87 | |
|---|
| 88 | def literals |
|---|
| 89 | # you've seen these above |
|---|
| 90 | ints = [1, 2, 3] |
|---|
| 91 | for i in ints, assert i > 0 |
|---|
| 92 | |
|---|
| 93 | names = ['foo', 'bar', 'baz'] |
|---|
| 94 | for name in names, assert name.length == 3 |
|---|
| 95 | |
|---|
| 96 | # list literals can span lines |
|---|
| 97 | names = [ |
|---|
| 98 | 'foo', |
|---|
| 99 | 'bar', |
|---|
| 100 | 'baz', # trailing comma is optional |
|---|
| 101 | ] |
|---|
| 102 | |
|---|
| 103 | # lists can be nested |
|---|
| 104 | pairs = [ |
|---|
| 105 | [0, 0], |
|---|
| 106 | [-2, -3], |
|---|
| 107 | [5, 2], |
|---|
| 108 | ] |
|---|
| 109 | for pair in pairs |
|---|
| 110 | assert pair.count == 2 |
|---|
| 111 | for x, y in pairs |
|---|
| 112 | assert x > -10 and y > -10 |
|---|
| 113 | |
|---|
| 114 | # lists can have other types of collections in them |
|---|
| 115 | sites = [ |
|---|
| 116 | {'name': 'Cobra', 'url': 'http://cobra-language.com/'}, |
|---|
| 117 | {'name': 'Wikipedia', 'url': 'http://en.wikipedia.org/'}, |
|---|
| 118 | ] |
|---|
| 119 | for dict in sites |
|---|
| 120 | assert dict.count == 2 |
|---|
| 121 | assert dict['url'].startsWith('http://') |
|---|
| 122 | |
|---|
| 123 | # lists can have object instantiations in them |
|---|
| 124 | points = [Point(0, 0), Point(1, 1), Point(0, 1), Point(1, 0)] |
|---|
| 125 | assert points[0].toString == 'Point(0, 0)' |
|---|
| 126 | |
|---|
| 127 | # lists can be heterogenous |
|---|
| 128 | stuff = ['foo', 5, nil] |
|---|
| 129 | assert stuff.reversed == [nil, 5, 'foo'] |
|---|
| 130 | |
|---|
| 131 | # the inferred type for a list is as narrow as possible |
|---|
| 132 | ints = [1, 2, 3] # List<of int> |
|---|
| 133 | names = ['foo', 'bar', 'baz'] # List<of String> |
|---|
| 134 | sites = [{'a': 'b'}] # List<of Dictionary<of String, String>> |
|---|
| 135 | points = [Point(0, 0)] # List<of Point> |
|---|
| 136 | stuff = ['foo', 5, nil] # List<of dynamic?> |
|---|
| 137 | stuff = [] # an empty list is List<of dynamic?> |
|---|
| 138 | |
|---|
| 139 | def stringConversion |
|---|
| 140 | ints = [1, 2, 3] |
|---|
| 141 | |
|---|
| 142 | # list to string is ugly in .NET |
|---|
| 143 | assert ints.toString == r'System.Collections.Generic.List`1[System.Int32]' |
|---|
| 144 | # cobra also provides |
|---|
| 145 | assert ints.toPrintString == r'[1, 2, 3]' |
|---|
| 146 | |
|---|
| 147 | # string substitution |
|---|
| 148 | assert 'the ints are [ints]' == r'the ints are [1, 2, 3]' |
|---|
| 149 | |
|---|
| 150 | # print |
|---|
| 151 | print ints # prints: [1, 2, 3] |
|---|
| 152 | |
|---|
| 153 | # print each element |
|---|
| 154 | for i in ints |
|---|
| 155 | print i |
|---|
| 156 | for index, i in ints.numbered |
|---|
| 157 | print '[index]. [i]' |
|---|
| 158 | |
|---|
| 159 | # trace |
|---|
| 160 | trace ints # trace: ints=List<of int>[1, 2, 3]; at x-how-to-list.cobra:163; |
|---|
| 161 | # in HowToUseLists.stringConversion |
|---|
| 162 | |
|---|
| 163 | # joining list elements into a string |
|---|
| 164 | assert ints.join(' ') == '1 2 3' |
|---|
| 165 | assert ints.join('---') == '1---2---3' |
|---|
| 166 | assert ints.join(', ', ' and ') == '1, 2 and 3' |
|---|
| 167 | |
|---|
| 168 | def operators |
|---|
| 169 | # you can concatenate two lists with + |
|---|
| 170 | # the original lists are not changed |
|---|
| 171 | a = [1, 2] |
|---|
| 172 | b = [3, 4] |
|---|
| 173 | assert a + b == [1, 2, 3, 4] |
|---|
| 174 | assert a == [1, 2] and b == [3, 4] |
|---|
| 175 | |
|---|
| 176 | # the expression `x in items` evaluates to true or false |
|---|
| 177 | # you can also write `x not in items` |
|---|
| 178 | ints = [1, 2, 3] |
|---|
| 179 | assert 1 in ints |
|---|
| 180 | assert 2 in ints and 3 in ints |
|---|
| 181 | assert 0 not in ints |
|---|
| 182 | assert 4 not in ints |
|---|
| 183 | |
|---|
| 184 | # the expression `all items` evaluates to true if every item is true |
|---|
| 185 | # the expression `any items` evaluates to true if any item is true |
|---|
| 186 | ints = [1, 2, 3] |
|---|
| 187 | assert all ints |
|---|
| 188 | assert any ints |
|---|
| 189 | ints = [0, 1, 2, 3] |
|---|
| 190 | assert not all ints |
|---|
| 191 | assert any ints |
|---|
| 192 | ints = [0, 0] |
|---|
| 193 | assert not all ints |
|---|
| 194 | assert not any ints |
|---|
| 195 | |
|---|
| 196 | # the equality operators can be used on lists |
|---|
| 197 | assert [1, 2] == [2, 1].reversed |
|---|
| 198 | assert [1, 2] <> [2, 1] |
|---|
| 199 | |
|---|
| 200 | def methods |
|---|
| 201 | # add an element to a list |
|---|
| 202 | names = ['foo', 'bar', 'baz'] |
|---|
| 203 | names.add('qux') |
|---|
| 204 | assert names == ['foo', 'bar', 'baz', 'qux'] |
|---|
| 205 | |
|---|
| 206 | # add multiple elements to a list |
|---|
| 207 | names.addRange(['x', 'y']) |
|---|
| 208 | assert names == ['foo', 'bar', 'baz', 'qux', 'x', 'y'] |
|---|
| 209 | |
|---|
| 210 | # insert an element |
|---|
| 211 | names.insert(1, 'fooey') |
|---|
| 212 | assert names == ['foo', 'fooey', 'bar', 'baz', 'qux', 'x', 'y'] |
|---|
| 213 | names.insert(7, 'z') # 7 is just past the end of the list |
|---|
| 214 | assert names == ['foo', 'fooey', 'bar', 'baz', 'qux', 'x', 'y', 'z'] |
|---|
| 215 | expect ArgumentOutOfRangeException, names.insert(20, 'exception') |
|---|
| 216 | |
|---|
| 217 | # clear a list |
|---|
| 218 | names.clear |
|---|
| 219 | assert names.count == 0 |
|---|
| 220 | |
|---|
| 221 | # find an item's index |
|---|
| 222 | names = ['foo', 'bar', 'baz', 'foo'] |
|---|
| 223 | assert names.indexOf('foo') == 0 |
|---|
| 224 | assert names.indexOf('baz') == 2 |
|---|
| 225 | assert names.indexOf('not there') == -1 |
|---|
| 226 | assert names.lastIndexOf('foo') == 3 |
|---|
| 227 | |
|---|
| 228 | # find an item's index in a subrange of the list |
|---|
| 229 | assert names.indexOf('foo', 1) == 3 # start at 1 |
|---|
| 230 | assert names.indexOf('foo', 1, 2) == -1 # start at 1, search 2 items |
|---|
| 231 | |
|---|
| 232 | # TODO: left off on .insertRange |
|---|
| 233 | |
|---|
| 234 | # additional methods. search web for "msdn generic list" |
|---|
| 235 | # .asReadOnly .binarySearch .convertAll .copyTo .exists .find .findAll .findIndex .findLast .findListIndex .forEach |
|---|
| 236 | |
|---|
| 237 | |
|---|
| 238 | |
|---|
| 239 | class Point |
|---|
| 240 | |
|---|
| 241 | cue init(x as int, y as int) |
|---|
| 242 | base.init |
|---|
| 243 | _x, _y = x, y |
|---|
| 244 | |
|---|
| 245 | def equals(other) as bool is override |
|---|
| 246 | if other is nil, return false |
|---|
| 247 | if other inherits Point, return .x == other.x and .y == other.y |
|---|
| 248 | return false |
|---|
| 249 | |
|---|
| 250 | def getHashCode as int is override |
|---|
| 251 | return .x ^ .y |
|---|
| 252 | |
|---|
| 253 | get x from var as int |
|---|
| 254 | |
|---|
| 255 | get y from var as int |
|---|
| 256 | |
|---|
| 257 | def toString as String is override |
|---|
| 258 | return '[.typeOf.name]([.x], [.y])' |
|---|
| 259 | |
|---|
| 260 | |
|---|
| 261 | # although it's not common to subclass the list type, |
|---|
| 262 | # you are certainly free to do so |
|---|
| 263 | |
|---|
| 264 | # a generic subclass |
|---|
| 265 | |
|---|
| 266 | class MyList<of T> inherits List<of T> |
|---|
| 267 | |
|---|
| 268 | get middle as T |
|---|
| 269 | require .count > 0 |
|---|
| 270 | return this[.count // 2] |
|---|
| 271 | |
|---|
| 272 | |
|---|
| 273 | # a subclass with specific type |
|---|
| 274 | |
|---|
| 275 | class PointList inherits List<of Point> |
|---|
| 276 | |
|---|
| 277 | test |
|---|
| 278 | pl = PointList() |
|---|
| 279 | pl.add(Point(1, 0)) |
|---|
| 280 | pl.add(Point(-2, 3)) |
|---|
| 281 | assert pl.count == 2 |
|---|
| 282 | assert pl.leftMost == Point(-2, 3) |
|---|
| 283 | |
|---|
| 284 | def leftMost as Point |
|---|
| 285 | require .count > 0 |
|---|
| 286 | leftMost = this[0] |
|---|
| 287 | for i in 1 : .count |
|---|
| 288 | if this[i].x < leftMost.x |
|---|
| 289 | leftMost = this[i] |
|---|
| 290 | return leftMost |
|---|
| 291 | |
|---|
| 292 | |
|---|
| 293 | # a generic subclass with a type constraint is even better, |
|---|
| 294 | # because then you can specify a type parameter when using it |
|---|
| 295 | |
|---|
| 296 | class PointList<of TPoint> inherits List<of TPoint> |
|---|
| 297 | where TPoint must be Point # the type constraint |
|---|
| 298 | |
|---|
| 299 | test |
|---|
| 300 | pl = PointList<of Point>() |
|---|
| 301 | pl.add(Point(1, 0)) |
|---|
| 302 | pl.add(Point(-2, 3)) |
|---|
| 303 | assert pl.count == 2 |
|---|
| 304 | assert pl.leftMost == Point(-2, 3) |
|---|
| 305 | |
|---|
| 306 | def leftMost as TPoint # notice the return type is TPoint |
|---|
| 307 | require .count > 0 |
|---|
| 308 | leftMost = this[0] |
|---|
| 309 | for i in 1 : .count |
|---|
| 310 | if this[i].x < leftMost.x |
|---|
| 311 | leftMost = this[i] |
|---|
| 312 | return leftMost |
|---|
| 313 | |
|---|
| 314 | # TODO: |
|---|
| 315 | |
|---|
| 316 | # slicing |
|---|
| 317 | |
|---|
| 318 | # customers.sort(ref .orderByTotalSpent) |
|---|
| 319 | # sort with do |
|---|
| 320 | |
|---|
| 321 | # declaring types |
|---|
| 322 | # List vs. IList |
|---|
| 323 | # List methods |
|---|
| 324 | # all extension methods |
|---|
| 325 | # check msdn docs on IList and List |
|---|
| 326 | # any enumerable .toList |
|---|
| 327 | # for expression returns a list |
|---|