slice.txt 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. Vector and string iteration are both 'special', in that they need to go *really* fast, and that
  2. their iterations are almost always numerically bounded and provably safe at entry-time. Constantly
  3. bounds-checking during iteration sucks and nobody likes it. You have to bounds-check on random
  4. access, but iteration on homogeneous bounded containers is a special case.
  5. Ideally we want a variant of the for loop:
  6. // mentioning 'vec' twice here is lame
  7. for vec (^auto elt = vec.elts(v)) {}
  8. // could drive it off the fact that vec.elts is a fn that returns a slice type?
  9. // possibly, but seems kludgey and non-obvious.
  10. for (^auto elt = vec.elts(v)) {}
  11. // how about an non-for keyword?
  12. step (^auto elt = vec.elts(v)) {}
  13. walk (^auto elt = vec.elts(v)) {}
  14. each (^auto elt = vec.elts(v)) {}
  15. span (^auto elt = vec.elts(v)) {}
  16. range (^auto elt = vec.elts(v)) {}
  17. slice (^auto elt = vec.elts(v)) {}
  18. iter (^auto elt = vec.elts(v)) {}
  19. hmm, that's possible. 'step' is not bad.
  20. Alternative: un-adorned 'for' currently (maybe) means '3-step c-style loop', which is almost always
  21. for a slice. so use un-adorned 'for'.
  22. for (^auto elt = vec.elts(v)) {}
  23. not bad. do we make slices a general bindable type though? it's tempting. they're useful:
  24. for! (^auto s = vec.slice(v,0,10)) { s is a slice in here; reads sorta ugly though. }
  25. for (^auto e = v.(0,10)) { e is an element in here }
  26. for (^auto e = permute.next(pstate, v)) { permute.next returns a slice, bare 'for' iterates it. }
  27. for (^auto e = v.(0,10,2)) { start, end, step }
  28. // most common idiom:
  29. for (^auto e = v.(,)) { 'iterate everything' }
  30. // 2nd-most common idiom (how do we distinguish them?):
  31. for (^auto (i,e) = v.(,)) { 'iterate everything' }
  32. // perhaps like this? reads *really* awkwardly:
  33. for (^auto e = v.(,);
  34. idx i = range(0,vec.len(v))) { 'iterate everything' }
  35. // how about as such?
  36. for (^auto (e,i) = vec.iteri(v)) { 'iteri' returns (slice,range) }
  37. Hmm. So there are several design issues at work simultaneously:
  38. - indexing to a slice, via extended indices: v.(start,end,step) is pretty handy.
  39. - pinning a slice -- or a single element for that matter -- using for! (vs what, 'let'?)
  40. - automatic iteration of a slice when used in an un-adorned 'for'.
  41. - getting the index of during a slice-iteration
  42. Ok, so let's assume we have a type called a range which is a 3-tuple
  43. (start,end,step) and you have a type called a slice[T] which comes from
  44. either a vec[T] or a str.
  45. Then we can say that the dot-indexing thing vec.(1,10,2) produces a slice,
  46. and that an unadorned for-loop does "homogeneous iteration" over any
  47. combination of same-count slices and ranges.
  48. Examples:
  49. for (^auto e = v.(,)) { e is walked over v }
  50. for (^auto i = (1,10)) { i is stepped through the range 1-to-10 }
  51. fn! iteri[T](^vec[T] v) -> (slice[T],range) { put (v.(,), (0,vec.len(v))); }
  52. for ((^T e, idx i) = iteri(v)) { e steps through v, i steps through range }
  53. This might work, it's tricky to get just right though: you want a slice to
  54. be something you establish through an iterator-fn that pins the vec and
  55. yields the slice. If a slice can only ever be established through such a
  56. binding... hm
  57. What does:
  58. slice[T] s = v.(,);
  59. do? I guess it could produce a CoW subvector that can itself be sliced?
  60. Well, we face this same dilemma with the general indexer actually. What
  61. does it mean to put a v.(i) normally? Its meaning is contingent on the
  62. put-type of the iterator.
  63. I guess the key question is how far a slice can be removed from its
  64. referent. We permit a v.(i) in put-position to index-into a vec mostly
  65. because the 'suspended form' v.(i) is a non-value; it's part of a name. The
  66. value is either a ~T or a ^T or a T, consistent with our memory model.
  67. A slice screws that up. It's "bound" do its base value in a 'write through'
  68. way.
  69. No, we can't do this. We can possibly do something close, but not
  70. a separable, denotable slice type.
  71. for (^auto (e,i) = (v.(,), vec.range(v))) { } ?
  72. for (idx i = vec.range(v));
  73. ^T e1 = v.(,);
  74. ^T e2 = u.(,)) {
  75. e2 = e1 * i;
  76. }
  77. for (~auto e = v.(,)) { out <| e <| endl; }
  78. that's actually not *that* bad. Then the slice-type exists but is strictly
  79. internal to the homogeneous for loop. And any range tuples are stepped along
  80. with it. All in parallel.
  81. nov 2009 thoughts:
  82. why not have CoW subvectors? don't worry about the use of an iterator-fn, just
  83. use the basic rule that any vec or str can have a new derived one formed that
  84. is a dependent (cheap) one.
  85. vec[int] v1 = vec(1, 2, 3, 4, 5, 6)
  86. vec[int] v2 = v1.(3,4)
  87. for (int i in v1) { ... stuff ... }
  88. for (int i in v2) { ... stuff ... }
  89. for (~int i in v2) { ... stuff ... }
  90. for (^int i in v2) { this one copies v2 out of v1 before running,
  91. *just like any multiply-referenced vec would* }