hamster 1.0.1.pre.rc2 → 1.0.1.pre.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (482) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hamster.rb +2 -2
  3. data/lib/hamster/core_ext.rb +0 -1
  4. data/lib/hamster/core_ext/enumerable.rb +17 -17
  5. data/lib/hamster/core_ext/io.rb +15 -17
  6. data/lib/hamster/deque.rb +229 -0
  7. data/lib/hamster/enumerable.rb +147 -105
  8. data/lib/hamster/experimental/mutable_queue.rb +2 -2
  9. data/lib/hamster/hash.rb +488 -82
  10. data/lib/hamster/immutable.rb +4 -0
  11. data/lib/hamster/list.rb +839 -196
  12. data/lib/hamster/read_copy_update.rb +1 -0
  13. data/lib/hamster/set.rb +317 -54
  14. data/lib/hamster/sorted_set.rb +1014 -0
  15. data/lib/hamster/trie.rb +67 -47
  16. data/lib/hamster/undefined.rb +1 -3
  17. data/lib/hamster/vector.rb +989 -76
  18. data/lib/hamster/version.rb +1 -1
  19. data/spec/{hamster → lib/hamster}/core_ext/array_spec.rb +1 -1
  20. data/spec/{hamster → lib/hamster}/core_ext/enumerable_spec.rb +4 -0
  21. data/spec/{hamster → lib/hamster}/core_ext/io_spec.rb +0 -0
  22. data/spec/lib/hamster/deque/clear_spec.rb +34 -0
  23. data/spec/lib/hamster/deque/construction_spec.rb +30 -0
  24. data/spec/lib/hamster/deque/copying_spec.rb +20 -0
  25. data/spec/lib/hamster/deque/dequeue_spec.rb +27 -0
  26. data/spec/lib/hamster/deque/empty_spec.rb +42 -0
  27. data/spec/{hamster/queue → lib/hamster/deque}/enqueue_spec.rb +7 -10
  28. data/spec/lib/hamster/deque/head_spec.rb +20 -0
  29. data/spec/lib/hamster/deque/inspect_spec.rb +24 -0
  30. data/spec/lib/hamster/deque/last_spec.rb +20 -0
  31. data/spec/lib/hamster/deque/marshal_spec.rb +34 -0
  32. data/spec/lib/hamster/deque/new_spec.rb +44 -0
  33. data/spec/lib/hamster/deque/pop_spec.rb +25 -0
  34. data/spec/lib/hamster/deque/random_modification_spec.rb +34 -0
  35. data/spec/{hamster/queue → lib/hamster/deque}/size_spec.rb +4 -9
  36. data/spec/lib/hamster/deque/to_a_spec.rb +27 -0
  37. data/spec/{hamster/queue → lib/hamster/deque}/to_ary_spec.rb +6 -6
  38. data/spec/lib/hamster/deque/to_list_spec.rb +26 -0
  39. data/spec/lib/hamster/deque/unshift_spec.rb +26 -0
  40. data/spec/{hamster → lib/hamster}/experimental/mutable_set/add_qm_spec.rb +0 -0
  41. data/spec/{hamster → lib/hamster}/experimental/mutable_set/add_spec.rb +0 -0
  42. data/spec/{hamster → lib/hamster}/experimental/mutable_set/delete_qm_spec.rb +0 -0
  43. data/spec/{hamster → lib/hamster}/experimental/mutable_set/delete_spec.rb +0 -0
  44. data/spec/{hamster → lib/hamster}/hash/all_spec.rb +10 -0
  45. data/spec/lib/hamster/hash/any_spec.rb +56 -0
  46. data/spec/lib/hamster/hash/assoc_spec.rb +52 -0
  47. data/spec/lib/hamster/hash/clear_spec.rb +43 -0
  48. data/spec/lib/hamster/hash/construction_spec.rb +39 -0
  49. data/spec/{hamster → lib/hamster}/hash/copying_spec.rb +2 -4
  50. data/spec/lib/hamster/hash/default_proc_spec.rb +73 -0
  51. data/spec/lib/hamster/hash/delete_spec.rb +40 -0
  52. data/spec/lib/hamster/hash/each_spec.rb +78 -0
  53. data/spec/lib/hamster/hash/each_with_index_spec.rb +30 -0
  54. data/spec/lib/hamster/hash/empty_spec.rb +46 -0
  55. data/spec/lib/hamster/hash/eql_spec.rb +70 -0
  56. data/spec/lib/hamster/hash/except_spec.rb +43 -0
  57. data/spec/lib/hamster/hash/fetch_spec.rb +58 -0
  58. data/spec/lib/hamster/hash/filter_spec.rb +58 -0
  59. data/spec/lib/hamster/hash/find_spec.rb +44 -0
  60. data/spec/lib/hamster/hash/flat_map_spec.rb +36 -0
  61. data/spec/lib/hamster/hash/flatten_spec.rb +99 -0
  62. data/spec/lib/hamster/hash/get_spec.rb +80 -0
  63. data/spec/lib/hamster/hash/has_key_spec.rb +32 -0
  64. data/spec/lib/hamster/hash/has_value_spec.rb +28 -0
  65. data/spec/{hamster → lib/hamster}/hash/hash_spec.rb +5 -12
  66. data/spec/{hamster → lib/hamster}/hash/immutable_spec.rb +0 -0
  67. data/spec/lib/hamster/hash/inspect_spec.rb +31 -0
  68. data/spec/lib/hamster/hash/invert_spec.rb +31 -0
  69. data/spec/lib/hamster/hash/key_spec.rb +28 -0
  70. data/spec/{hamster → lib/hamster}/hash/keys_spec.rb +3 -6
  71. data/spec/lib/hamster/hash/map_spec.rb +46 -0
  72. data/spec/{hamster → lib/hamster}/hash/marshal_spec.rb +3 -3
  73. data/spec/lib/hamster/hash/merge_spec.rb +77 -0
  74. data/spec/lib/hamster/hash/min_max_spec.rb +50 -0
  75. data/spec/lib/hamster/hash/new_spec.rb +71 -0
  76. data/spec/{hamster → lib/hamster}/hash/none_spec.rb +12 -14
  77. data/spec/lib/hamster/hash/partition_spec.rb +36 -0
  78. data/spec/lib/hamster/hash/pretty_print_spec.rb +35 -0
  79. data/spec/lib/hamster/hash/put_spec.rb +81 -0
  80. data/spec/lib/hamster/hash/reduce_spec.rb +36 -0
  81. data/spec/lib/hamster/hash/remove_spec.rb +62 -0
  82. data/spec/lib/hamster/hash/reverse_each_spec.rb +28 -0
  83. data/spec/lib/hamster/hash/sample_spec.rb +14 -0
  84. data/spec/{hamster → lib/hamster}/hash/size_spec.rb +2 -2
  85. data/spec/lib/hamster/hash/slice_spec.rb +45 -0
  86. data/spec/lib/hamster/hash/sort_spec.rb +27 -0
  87. data/spec/lib/hamster/hash/store_spec.rb +54 -0
  88. data/spec/lib/hamster/hash/take_spec.rb +36 -0
  89. data/spec/lib/hamster/hash/to_a_spec.rb +14 -0
  90. data/spec/lib/hamster/hash/to_hash_spec.rb +22 -0
  91. data/spec/{hamster → lib/hamster}/hash/uniq_spec.rb +2 -4
  92. data/spec/lib/hamster/hash/values_at_spec.rb +14 -0
  93. data/spec/lib/hamster/hash/values_spec.rb +25 -0
  94. data/spec/{hamster → lib/hamster}/immutable/copying_spec.rb +0 -0
  95. data/spec/{hamster → lib/hamster}/immutable/immutable_spec.rb +0 -0
  96. data/spec/{hamster → lib/hamster}/immutable/memoize_spec.rb +2 -2
  97. data/spec/{hamster → lib/hamster}/immutable/new_spec.rb +0 -0
  98. data/spec/{hamster → lib/hamster}/immutable/transform_spec.rb +0 -0
  99. data/spec/{hamster → lib/hamster}/immutable/transform_unless_spec.rb +0 -0
  100. data/spec/lib/hamster/list/add_spec.rb +20 -0
  101. data/spec/lib/hamster/list/all_spec.rb +60 -0
  102. data/spec/{hamster → lib/hamster}/list/any_spec.rb +12 -20
  103. data/spec/{hamster → lib/hamster}/list/append_spec.rb +9 -10
  104. data/spec/lib/hamster/list/at_spec.rb +30 -0
  105. data/spec/lib/hamster/list/break_spec.rb +70 -0
  106. data/spec/{hamster → lib/hamster}/list/cadr_spec.rb +5 -8
  107. data/spec/{hamster → lib/hamster}/list/chunk_spec.rb +5 -8
  108. data/spec/{hamster → lib/hamster}/list/clear_spec.rb +4 -7
  109. data/spec/lib/hamster/list/combination_spec.rb +34 -0
  110. data/spec/{hamster → lib/hamster}/list/compact_spec.rb +5 -8
  111. data/spec/lib/hamster/list/compare_spec.rb +31 -0
  112. data/spec/{hamster → lib/hamster}/list/cons_spec.rb +5 -9
  113. data/spec/lib/hamster/list/construction_spec.rb +118 -0
  114. data/spec/{hamster → lib/hamster}/list/copying_spec.rb +3 -7
  115. data/spec/lib/hamster/list/count_spec.rb +37 -0
  116. data/spec/lib/hamster/list/cycle_spec.rb +29 -0
  117. data/spec/lib/hamster/list/delete_at_spec.rb +19 -0
  118. data/spec/lib/hamster/list/delete_spec.rb +17 -0
  119. data/spec/{hamster → lib/hamster}/list/drop_spec.rb +5 -8
  120. data/spec/lib/hamster/list/drop_while_spec.rb +39 -0
  121. data/spec/lib/hamster/list/each_slice_spec.rb +52 -0
  122. data/spec/lib/hamster/list/each_spec.rb +43 -0
  123. data/spec/lib/hamster/list/each_with_index_spec.rb +29 -0
  124. data/spec/{hamster → lib/hamster}/list/elem_index_spec.rb +4 -14
  125. data/spec/{hamster → lib/hamster}/list/elem_indices_spec.rb +5 -9
  126. data/spec/{hamster → lib/hamster}/list/empty_spec.rb +4 -13
  127. data/spec/{hamster → lib/hamster}/list/eql_spec.rb +2 -8
  128. data/spec/lib/hamster/list/fill_spec.rb +50 -0
  129. data/spec/{hamster → lib/hamster}/list/filter_spec.rb +3 -2
  130. data/spec/{hamster → lib/hamster}/list/find_all_spec.rb +3 -2
  131. data/spec/{hamster → lib/hamster}/list/find_index_spec.rb +4 -14
  132. data/spec/{hamster → lib/hamster}/list/find_indices_spec.rb +11 -9
  133. data/spec/{hamster → lib/hamster}/list/find_spec.rb +10 -16
  134. data/spec/{hamster → lib/hamster}/list/flat_map_spec.rb +0 -0
  135. data/spec/{hamster → lib/hamster}/list/flatten_spec.rb +5 -8
  136. data/spec/{hamster → lib/hamster}/list/grep_spec.rb +9 -17
  137. data/spec/{hamster → lib/hamster}/list/group_by_spec.rb +8 -24
  138. data/spec/{hamster → lib/hamster}/list/hash_spec.rb +4 -12
  139. data/spec/{hamster → lib/hamster}/list/head_spec.rb +2 -7
  140. data/spec/{hamster → lib/hamster}/list/include_spec.rb +4 -13
  141. data/spec/{hamster → lib/hamster}/list/init_spec.rb +5 -9
  142. data/spec/lib/hamster/list/inits_spec.rb +29 -0
  143. data/spec/lib/hamster/list/insert_spec.rb +47 -0
  144. data/spec/lib/hamster/list/inspect_spec.rb +30 -0
  145. data/spec/{hamster → lib/hamster}/list/intersperse_spec.rb +5 -8
  146. data/spec/lib/hamster/list/join_spec.rb +64 -0
  147. data/spec/lib/hamster/list/last_spec.rb +24 -0
  148. data/spec/{hamster → lib/hamster}/list/map_spec.rb +11 -20
  149. data/spec/lib/hamster/list/maximum_spec.rb +42 -0
  150. data/spec/{hamster → lib/hamster}/list/merge_by_spec.rb +4 -13
  151. data/spec/{hamster → lib/hamster}/list/merge_spec.rb +0 -0
  152. data/spec/lib/hamster/list/minimum_spec.rb +42 -0
  153. data/spec/lib/hamster/list/multithreading_spec.rb +48 -0
  154. data/spec/{hamster → lib/hamster}/list/none_spec.rb +11 -21
  155. data/spec/{hamster → lib/hamster}/list/one_spec.rb +12 -22
  156. data/spec/lib/hamster/list/partition_spec.rb +116 -0
  157. data/spec/lib/hamster/list/permutation_spec.rb +56 -0
  158. data/spec/{hamster → lib/hamster}/list/pop_spec.rb +1 -1
  159. data/spec/lib/hamster/list/product_spec.rb +24 -0
  160. data/spec/lib/hamster/list/reduce_spec.rb +97 -0
  161. data/spec/{hamster → lib/hamster}/list/remove_spec.rb +9 -19
  162. data/spec/{hamster → lib/hamster}/list/reverse_spec.rb +7 -14
  163. data/spec/lib/hamster/list/rotate_spec.rb +37 -0
  164. data/spec/lib/hamster/list/sample_spec.rb +14 -0
  165. data/spec/{hamster → lib/hamster}/list/select_spec.rb +3 -2
  166. data/spec/{hamster → lib/hamster}/list/size_spec.rb +4 -13
  167. data/spec/lib/hamster/list/slice_spec.rb +230 -0
  168. data/spec/{hamster → lib/hamster}/list/sorting_spec.rb +10 -20
  169. data/spec/lib/hamster/list/span_spec.rb +77 -0
  170. data/spec/{hamster → lib/hamster}/list/split_at_spec.rb +13 -14
  171. data/spec/lib/hamster/list/subsequences_spec.rb +24 -0
  172. data/spec/lib/hamster/list/sum_spec.rb +24 -0
  173. data/spec/lib/hamster/list/tail_spec.rb +31 -0
  174. data/spec/lib/hamster/list/tails_spec.rb +29 -0
  175. data/spec/{hamster → lib/hamster}/list/take_spec.rb +5 -8
  176. data/spec/{hamster → lib/hamster}/list/take_while_spec.rb +11 -17
  177. data/spec/lib/hamster/list/to_a_spec.rb +40 -0
  178. data/spec/{hamster → lib/hamster}/list/to_ary_spec.rb +1 -3
  179. data/spec/{hamster → lib/hamster}/list/to_list_spec.rb +3 -7
  180. data/spec/{hamster → lib/hamster}/list/to_set_spec.rb +2 -10
  181. data/spec/lib/hamster/list/transpose_spec.rb +20 -0
  182. data/spec/{hamster → lib/hamster}/list/union_spec.rb +5 -12
  183. data/spec/{hamster → lib/hamster}/list/uniq_spec.rb +5 -8
  184. data/spec/{hamster → lib/hamster}/list/zip_spec.rb +2 -9
  185. data/spec/lib/hamster/set/add_spec.rb +76 -0
  186. data/spec/{hamster → lib/hamster}/set/all_spec.rb +18 -14
  187. data/spec/{hamster → lib/hamster}/set/any_spec.rb +19 -16
  188. data/spec/{hamster → lib/hamster}/set/clear_spec.rb +8 -11
  189. data/spec/{hamster → lib/hamster}/set/compact_spec.rb +4 -7
  190. data/spec/{hamster → lib/hamster}/set/construction_spec.rb +4 -8
  191. data/spec/{hamster → lib/hamster}/set/copying_spec.rb +3 -5
  192. data/spec/{hamster → lib/hamster}/set/count_spec.rb +11 -16
  193. data/spec/lib/hamster/set/delete_spec.rb +72 -0
  194. data/spec/lib/hamster/set/difference_spec.rb +50 -0
  195. data/spec/lib/hamster/set/disjoint_spec.rb +26 -0
  196. data/spec/lib/hamster/set/each_spec.rb +46 -0
  197. data/spec/lib/hamster/set/empty_spec.rb +47 -0
  198. data/spec/{hamster → lib/hamster}/set/eqeq_spec.rb +0 -0
  199. data/spec/{hamster → lib/hamster}/set/eql_spec.rb +7 -1
  200. data/spec/lib/hamster/set/exclusion_spec.rb +48 -0
  201. data/spec/lib/hamster/set/filter_spec.rb +74 -0
  202. data/spec/{hamster → lib/hamster}/set/find_spec.rb +7 -11
  203. data/spec/lib/hamster/set/flatten_spec.rb +47 -0
  204. data/spec/{hamster → lib/hamster}/set/foreach_spec.rb +5 -4
  205. data/spec/{hamster → lib/hamster}/set/grep_spec.rb +1 -1
  206. data/spec/lib/hamster/set/group_by_spec.rb +60 -0
  207. data/spec/lib/hamster/set/hash_spec.rb +23 -0
  208. data/spec/lib/hamster/set/head_spec.rb +31 -0
  209. data/spec/{hamster → lib/hamster}/set/immutable_spec.rb +0 -0
  210. data/spec/lib/hamster/set/include_spec.rb +61 -0
  211. data/spec/lib/hamster/set/inspect_spec.rb +48 -0
  212. data/spec/lib/hamster/set/intersect_spec.rb +26 -0
  213. data/spec/lib/hamster/set/intersection_spec.rb +53 -0
  214. data/spec/lib/hamster/set/join_spec.rb +65 -0
  215. data/spec/lib/hamster/set/map_spec.rb +60 -0
  216. data/spec/{hamster → lib/hamster}/set/marshal_spec.rb +3 -3
  217. data/spec/{hamster → lib/hamster}/set/maximum_spec.rb +6 -15
  218. data/spec/{hamster → lib/hamster}/set/minimum_spec.rb +6 -16
  219. data/spec/lib/hamster/set/new_spec.rb +54 -0
  220. data/spec/{hamster → lib/hamster}/set/none_spec.rb +15 -15
  221. data/spec/{hamster → lib/hamster}/set/one_spec.rb +14 -16
  222. data/spec/lib/hamster/set/partition_spec.rb +53 -0
  223. data/spec/{hamster → lib/hamster}/set/product_spec.rb +6 -6
  224. data/spec/lib/hamster/set/reduce_spec.rb +56 -0
  225. data/spec/lib/hamster/set/remove_spec.rb +51 -0
  226. data/spec/{hamster/set/each_spec.rb → lib/hamster/set/reverse_each_spec.rb} +8 -7
  227. data/spec/lib/hamster/set/sample_spec.rb +14 -0
  228. data/spec/{hamster → lib/hamster}/set/size_spec.rb +0 -1
  229. data/spec/{hamster → lib/hamster}/set/sorting_spec.rb +17 -13
  230. data/spec/lib/hamster/set/subset_spec.rb +52 -0
  231. data/spec/lib/hamster/set/sum_spec.rb +24 -0
  232. data/spec/lib/hamster/set/superset_spec.rb +52 -0
  233. data/spec/lib/hamster/set/to_a_spec.rb +31 -0
  234. data/spec/lib/hamster/set/to_list_spec.rb +37 -0
  235. data/spec/{hamster → lib/hamster}/set/to_set_spec.rb +2 -6
  236. data/spec/lib/hamster/set/union_spec.rb +55 -0
  237. data/spec/{hamster → lib/hamster}/set/uniq_spec.rb +2 -5
  238. data/spec/lib/hamster/sorted_set/above_spec.rb +52 -0
  239. data/spec/lib/hamster/sorted_set/add_spec.rb +63 -0
  240. data/spec/lib/hamster/sorted_set/at_spec.rb +25 -0
  241. data/spec/lib/hamster/sorted_set/below_spec.rb +52 -0
  242. data/spec/lib/hamster/sorted_set/between_spec.rb +52 -0
  243. data/spec/lib/hamster/sorted_set/clear_spec.rb +35 -0
  244. data/spec/lib/hamster/sorted_set/construction_spec.rb +29 -0
  245. data/spec/lib/hamster/sorted_set/delete_at_spec.rb +19 -0
  246. data/spec/lib/hamster/sorted_set/delete_spec.rb +81 -0
  247. data/spec/{hamster/set → lib/hamster/sorted_set}/difference_spec.rb +5 -9
  248. data/spec/lib/hamster/sorted_set/disjoint_spec.rb +26 -0
  249. data/spec/lib/hamster/sorted_set/drop_spec.rb +29 -0
  250. data/spec/lib/hamster/sorted_set/drop_while_spec.rb +35 -0
  251. data/spec/lib/hamster/sorted_set/each_spec.rb +31 -0
  252. data/spec/lib/hamster/sorted_set/empty_spec.rb +37 -0
  253. data/spec/lib/hamster/sorted_set/eql_spec.rb +121 -0
  254. data/spec/{hamster/set → lib/hamster/sorted_set}/exclusion_spec.rb +4 -9
  255. data/spec/lib/hamster/sorted_set/fetch_spec.rb +65 -0
  256. data/spec/lib/hamster/sorted_set/filter_spec.rb +62 -0
  257. data/spec/lib/hamster/sorted_set/find_index_spec.rb +33 -0
  258. data/spec/lib/hamster/sorted_set/first_spec.rb +21 -0
  259. data/spec/lib/hamster/sorted_set/from_spec.rb +52 -0
  260. data/spec/lib/hamster/sorted_set/group_by_spec.rb +58 -0
  261. data/spec/lib/hamster/sorted_set/include_spec.rb +24 -0
  262. data/spec/lib/hamster/sorted_set/inspect_spec.rb +38 -0
  263. data/spec/lib/hamster/sorted_set/intersect_spec.rb +26 -0
  264. data/spec/lib/hamster/sorted_set/intersection_spec.rb +29 -0
  265. data/spec/lib/hamster/sorted_set/last_spec.rb +37 -0
  266. data/spec/lib/hamster/sorted_set/map_spec.rb +36 -0
  267. data/spec/lib/hamster/sorted_set/marshal_spec.rb +37 -0
  268. data/spec/lib/hamster/sorted_set/minimum_spec.rb +22 -0
  269. data/spec/lib/hamster/sorted_set/new_spec.rb +52 -0
  270. data/spec/lib/hamster/sorted_set/reverse_each_spec.rb +29 -0
  271. data/spec/lib/hamster/sorted_set/sample_spec.rb +14 -0
  272. data/spec/lib/hamster/sorted_set/size_spec.rb +18 -0
  273. data/spec/lib/hamster/sorted_set/slice_spec.rb +241 -0
  274. data/spec/lib/hamster/sorted_set/sorting_spec.rb +45 -0
  275. data/spec/lib/hamster/sorted_set/subset_spec.rb +48 -0
  276. data/spec/lib/hamster/sorted_set/superset_spec.rb +48 -0
  277. data/spec/lib/hamster/sorted_set/take_spec.rb +26 -0
  278. data/spec/lib/hamster/sorted_set/take_while_spec.rb +34 -0
  279. data/spec/lib/hamster/sorted_set/to_set_spec.rb +19 -0
  280. data/spec/lib/hamster/sorted_set/union_spec.rb +28 -0
  281. data/spec/lib/hamster/sorted_set/up_to_spec.rb +52 -0
  282. data/spec/lib/hamster/sorted_set/values_at_spec.rb +34 -0
  283. data/spec/lib/hamster/vector/add_spec.rb +68 -0
  284. data/spec/{hamster → lib/hamster}/vector/any_spec.rb +0 -0
  285. data/spec/lib/hamster/vector/assoc_spec.rb +36 -0
  286. data/spec/lib/hamster/vector/bsearch_spec.rb +58 -0
  287. data/spec/lib/hamster/vector/clear_spec.rb +34 -0
  288. data/spec/lib/hamster/vector/combination_spec.rb +82 -0
  289. data/spec/lib/hamster/vector/compact_spec.rb +30 -0
  290. data/spec/lib/hamster/vector/compare_spec.rb +32 -0
  291. data/spec/lib/hamster/vector/concat_spec.rb +35 -0
  292. data/spec/{hamster → lib/hamster}/vector/copying_spec.rb +3 -6
  293. data/spec/lib/hamster/vector/count_spec.rb +18 -0
  294. data/spec/lib/hamster/vector/delete_at_spec.rb +54 -0
  295. data/spec/lib/hamster/vector/delete_spec.rb +31 -0
  296. data/spec/lib/hamster/vector/drop_spec.rb +35 -0
  297. data/spec/lib/hamster/vector/drop_while_spec.rb +55 -0
  298. data/spec/lib/hamster/vector/each_index_spec.rb +41 -0
  299. data/spec/lib/hamster/vector/each_spec.rb +47 -0
  300. data/spec/lib/hamster/vector/each_with_index_spec.rb +40 -0
  301. data/spec/lib/hamster/vector/empty_spec.rb +44 -0
  302. data/spec/lib/hamster/vector/eql_spec.rb +77 -0
  303. data/spec/lib/hamster/vector/exist_spec.rb +4 -4
  304. data/spec/lib/hamster/vector/exists_spec.rb +4 -4
  305. data/spec/lib/hamster/vector/fetch_spec.rb +65 -0
  306. data/spec/lib/hamster/vector/fill_spec.rb +89 -0
  307. data/spec/lib/hamster/vector/filter_spec.rb +64 -0
  308. data/spec/{hamster → lib/hamster}/vector/first_spec.rb +2 -6
  309. data/spec/lib/hamster/vector/flatten_spec.rb +44 -0
  310. data/spec/lib/hamster/vector/get_spec.rb +75 -0
  311. data/spec/lib/hamster/vector/group_by_spec.rb +58 -0
  312. data/spec/{hamster → lib/hamster}/vector/include_spec.rb +2 -6
  313. data/spec/lib/hamster/vector/insert_spec.rb +69 -0
  314. data/spec/lib/hamster/vector/inspect_spec.rb +50 -0
  315. data/spec/{hamster/set → lib/hamster/vector}/join_spec.rb +23 -18
  316. data/spec/{hamster → lib/hamster}/vector/last_spec.rb +12 -3
  317. data/spec/{hamster → lib/hamster}/vector/length_spec.rb +12 -3
  318. data/spec/lib/hamster/vector/ltlt_spec.rb +20 -2
  319. data/spec/lib/hamster/vector/map_spec.rb +52 -0
  320. data/spec/lib/hamster/vector/marshal_spec.rb +32 -0
  321. data/spec/lib/hamster/vector/maximum_spec.rb +36 -0
  322. data/spec/lib/hamster/vector/minimum_spec.rb +36 -0
  323. data/spec/lib/hamster/vector/multiply_spec.rb +48 -0
  324. data/spec/lib/hamster/vector/new_spec.rb +51 -0
  325. data/spec/lib/hamster/vector/partition_spec.rb +53 -0
  326. data/spec/lib/hamster/vector/permutation_spec.rb +92 -0
  327. data/spec/lib/hamster/vector/pop_spec.rb +27 -0
  328. data/spec/lib/hamster/vector/product_spec.rb +71 -0
  329. data/spec/lib/hamster/vector/reduce_spec.rb +108 -0
  330. data/spec/lib/hamster/vector/remove_spec.rb +44 -0
  331. data/spec/lib/hamster/vector/repeated_combination_spec.rb +78 -0
  332. data/spec/lib/hamster/vector/repeated_permutation_spec.rb +94 -0
  333. data/spec/lib/hamster/vector/reverse_each_spec.rb +32 -0
  334. data/spec/lib/hamster/vector/reverse_spec.rb +22 -0
  335. data/spec/lib/hamster/vector/rindex_spec.rb +37 -0
  336. data/spec/lib/hamster/vector/rotate_spec.rb +74 -0
  337. data/spec/lib/hamster/vector/sample_spec.rb +14 -0
  338. data/spec/{hamster → lib/hamster}/vector/set_spec.rb +48 -14
  339. data/spec/lib/hamster/vector/shift_spec.rb +28 -0
  340. data/spec/lib/hamster/vector/shuffle_spec.rb +44 -0
  341. data/spec/lib/hamster/vector/slice_spec.rb +241 -0
  342. data/spec/lib/hamster/vector/sorting_spec.rb +57 -0
  343. data/spec/{hamster/set → lib/hamster/vector}/sum_spec.rb +3 -9
  344. data/spec/lib/hamster/vector/take_spec.rb +29 -0
  345. data/spec/lib/hamster/vector/take_while_spec.rb +35 -0
  346. data/spec/{hamster → lib/hamster}/vector/to_a_spec.rb +11 -12
  347. data/spec/{hamster → lib/hamster}/vector/to_ary_spec.rb +0 -0
  348. data/spec/{hamster/set → lib/hamster/vector}/to_list_spec.rb +7 -12
  349. data/spec/lib/hamster/vector/to_set_spec.rb +23 -0
  350. data/spec/lib/hamster/vector/transpose_spec.rb +49 -0
  351. data/spec/lib/hamster/vector/uniq_spec.rb +56 -0
  352. data/spec/lib/hamster/vector/unshift_spec.rb +29 -0
  353. data/spec/lib/hamster/vector/values_at_spec.rb +34 -0
  354. data/spec/lib/hamster/vector/zip_spec.rb +58 -0
  355. data/spec/spec_helper.rb +34 -1
  356. metadata +684 -467
  357. data/lib/hamster/core_ext/enumerator.rb +0 -16
  358. data/lib/hamster/experimental/mutable_stack.rb +0 -30
  359. data/lib/hamster/groupable.rb +0 -12
  360. data/lib/hamster/queue.rb +0 -86
  361. data/lib/hamster/sorter.rb +0 -25
  362. data/lib/hamster/stack.rb +0 -77
  363. data/lib/hamster/tuple.rb +0 -24
  364. data/spec/hamster/core_ext/enumerator_spec.rb +0 -19
  365. data/spec/hamster/experimental/mutable_stack/pop_spec.rb +0 -35
  366. data/spec/hamster/experimental/mutable_stack/push_spec.rb +0 -21
  367. data/spec/hamster/hash/any_spec.rb +0 -52
  368. data/spec/hamster/hash/clear_spec.rb +0 -29
  369. data/spec/hamster/hash/construction_spec.rb +0 -27
  370. data/spec/hamster/hash/delete_spec.rb +0 -38
  371. data/spec/hamster/hash/each_spec.rb +0 -30
  372. data/spec/hamster/hash/empty_spec.rb +0 -27
  373. data/spec/hamster/hash/eql_spec.rb +0 -70
  374. data/spec/hamster/hash/except_spec.rb +0 -22
  375. data/spec/hamster/hash/fetch_spec.rb +0 -72
  376. data/spec/hamster/hash/filter_spec.rb +0 -48
  377. data/spec/hamster/hash/find_spec.rb +0 -45
  378. data/spec/hamster/hash/get_spec.rb +0 -55
  379. data/spec/hamster/hash/has_key_spec.rb +0 -26
  380. data/spec/hamster/hash/inspect_spec.rb +0 -24
  381. data/spec/hamster/hash/map_spec.rb +0 -49
  382. data/spec/hamster/hash/merge_spec.rb +0 -30
  383. data/spec/hamster/hash/new_spec.rb +0 -21
  384. data/spec/hamster/hash/put_spec.rb +0 -67
  385. data/spec/hamster/hash/reduce_spec.rb +0 -52
  386. data/spec/hamster/hash/remove_spec.rb +0 -48
  387. data/spec/hamster/hash/slice_spec.rb +0 -26
  388. data/spec/hamster/hash/values_spec.rb +0 -29
  389. data/spec/hamster/list/add_spec.rb +0 -11
  390. data/spec/hamster/list/all_spec.rb +0 -84
  391. data/spec/hamster/list/at_spec.rb +0 -37
  392. data/spec/hamster/list/break_spec.rb +0 -73
  393. data/spec/hamster/list/combinations_spec.rb +0 -41
  394. data/spec/hamster/list/construction_spec.rb +0 -137
  395. data/spec/hamster/list/count_spec.rb +0 -52
  396. data/spec/hamster/list/cycle_spec.rb +0 -36
  397. data/spec/hamster/list/drop_while_spec.rb +0 -47
  398. data/spec/hamster/list/each_slice_spec.rb +0 -64
  399. data/spec/hamster/list/each_spec.rb +0 -56
  400. data/spec/hamster/list/each_with_index_spec.rb +0 -33
  401. data/spec/hamster/list/inits_spec.rb +0 -34
  402. data/spec/hamster/list/inspect_spec.rb +0 -33
  403. data/spec/hamster/list/join_spec.rb +0 -64
  404. data/spec/hamster/list/last_spec.rb +0 -34
  405. data/spec/hamster/list/maximum_spec.rb +0 -58
  406. data/spec/hamster/list/minimum_spec.rb +0 -58
  407. data/spec/hamster/list/partition_spec.rb +0 -63
  408. data/spec/hamster/list/product_spec.rb +0 -34
  409. data/spec/hamster/list/reduce_spec.rb +0 -72
  410. data/spec/hamster/list/slice_spec.rb +0 -40
  411. data/spec/hamster/list/span_spec.rb +0 -75
  412. data/spec/hamster/list/sum_spec.rb +0 -34
  413. data/spec/hamster/list/tail_spec.rb +0 -38
  414. data/spec/hamster/list/tails_spec.rb +0 -34
  415. data/spec/hamster/list/to_a_spec.rb +0 -42
  416. data/spec/hamster/queue/clear_spec.rb +0 -28
  417. data/spec/hamster/queue/construction_spec.rb +0 -34
  418. data/spec/hamster/queue/dequeue_spec.rb +0 -30
  419. data/spec/hamster/queue/empty_spec.rb +0 -35
  420. data/spec/hamster/queue/head_spec.rb +0 -25
  421. data/spec/hamster/queue/inspect_spec.rb +0 -23
  422. data/spec/hamster/queue/to_a_spec.rb +0 -32
  423. data/spec/hamster/queue/to_list_spec.rb +0 -34
  424. data/spec/hamster/set/add_spec.rb +0 -40
  425. data/spec/hamster/set/delete_spec.rb +0 -38
  426. data/spec/hamster/set/empty_spec.rb +0 -25
  427. data/spec/hamster/set/filter_spec.rb +0 -72
  428. data/spec/hamster/set/flatten_spec.rb +0 -47
  429. data/spec/hamster/set/group_by_spec.rb +0 -56
  430. data/spec/hamster/set/hash_spec.rb +0 -20
  431. data/spec/hamster/set/head_spec.rb +0 -28
  432. data/spec/hamster/set/include_spec.rb +0 -27
  433. data/spec/hamster/set/inspect_spec.rb +0 -24
  434. data/spec/hamster/set/intersection_spec.rb +0 -36
  435. data/spec/hamster/set/map_spec.rb +0 -49
  436. data/spec/hamster/set/new_spec.rb +0 -21
  437. data/spec/hamster/set/partition_spec.rb +0 -59
  438. data/spec/hamster/set/reduce_spec.rb +0 -62
  439. data/spec/hamster/set/remove_spec.rb +0 -48
  440. data/spec/hamster/set/subset_spec.rb +0 -31
  441. data/spec/hamster/set/superset_spec.rb +0 -31
  442. data/spec/hamster/set/to_a_spec.rb +0 -32
  443. data/spec/hamster/set/union_spec.rb +0 -35
  444. data/spec/hamster/sorter/immutable_spec.rb +0 -9
  445. data/spec/hamster/stack/clear_spec.rb +0 -28
  446. data/spec/hamster/stack/construction_spec.rb +0 -34
  447. data/spec/hamster/stack/copying_spec.rb +0 -23
  448. data/spec/hamster/stack/empty_spec.rb +0 -23
  449. data/spec/hamster/stack/eql_spec.rb +0 -48
  450. data/spec/hamster/stack/immutable_spec.rb +0 -9
  451. data/spec/hamster/stack/inspect_spec.rb +0 -23
  452. data/spec/hamster/stack/peek_spec.rb +0 -30
  453. data/spec/hamster/stack/pop_spec.rb +0 -31
  454. data/spec/hamster/stack/push_spec.rb +0 -31
  455. data/spec/hamster/stack/size_spec.rb +0 -25
  456. data/spec/hamster/stack/to_a_spec.rb +0 -32
  457. data/spec/hamster/stack/to_ary.rb +0 -37
  458. data/spec/hamster/stack/to_list_spec.rb +0 -25
  459. data/spec/hamster/trie/remove_spec.rb +0 -117
  460. data/spec/hamster/tuple/construction_spec.rb +0 -30
  461. data/spec/hamster/tuple/copying_spec.rb +0 -17
  462. data/spec/hamster/tuple/eql_spec.rb +0 -78
  463. data/spec/hamster/tuple/first_spec.rb +0 -14
  464. data/spec/hamster/tuple/immutable_spec.rb +0 -9
  465. data/spec/hamster/tuple/inspect_spec.rb +0 -14
  466. data/spec/hamster/tuple/last_spec.rb +0 -14
  467. data/spec/hamster/tuple/to_a_spec.rb +0 -30
  468. data/spec/hamster/tuple/to_ary_spec.rb +0 -37
  469. data/spec/hamster/undefined/erase_spec.rb +0 -36
  470. data/spec/hamster/vector/add_spec.rb +0 -56
  471. data/spec/hamster/vector/clear_spec.rb +0 -28
  472. data/spec/hamster/vector/each_spec.rb +0 -35
  473. data/spec/hamster/vector/each_with_index_spec.rb +0 -33
  474. data/spec/hamster/vector/empty_spec.rb +0 -32
  475. data/spec/hamster/vector/eql_spec.rb +0 -53
  476. data/spec/hamster/vector/filter_spec.rb +0 -58
  477. data/spec/hamster/vector/get_spec.rb +0 -58
  478. data/spec/hamster/vector/inspect_spec.rb +0 -33
  479. data/spec/hamster/vector/map_spec.rb +0 -57
  480. data/spec/hamster/vector/new_spec.rb +0 -48
  481. data/spec/hamster/vector/reduce_spec.rb +0 -62
  482. data/spec/lib/hamster/vector/cons_spec.rb +0 -48
@@ -1,4 +1,5 @@
1
1
  module Hamster
2
+ # @private
2
3
  module Immutable
3
4
  def self.included(klass)
4
5
  klass.extend(ClassMethods)
@@ -7,6 +8,7 @@ module Hamster
7
8
  end
8
9
  end
9
10
 
11
+ # @private
10
12
  module ClassMethods
11
13
  def new(*args)
12
14
  super.immutable!
@@ -30,6 +32,7 @@ module Hamster
30
32
  end
31
33
  end
32
34
 
35
+ # @private
33
36
  module MemoizeMethods
34
37
  def immutable!
35
38
  @__hamster_immutable_memory__ = Object.new
@@ -37,6 +40,7 @@ module Hamster
37
40
  end
38
41
  end
39
42
 
43
+ # @private
40
44
  module InstanceMethods
41
45
  def immutable!
42
46
  freeze
@@ -1,49 +1,43 @@
1
1
  require "forwardable"
2
2
  require "thread"
3
+ require "atomic"
4
+ require "set"
3
5
 
4
6
  require "hamster/core_ext/enumerable"
5
7
  require "hamster/undefined"
6
8
  require "hamster/enumerable"
7
- require "hamster/groupable"
8
- require "hamster/tuple"
9
- require "hamster/sorter"
10
- require "hamster/hash"
11
9
  require "hamster/set"
12
10
 
13
11
  module Hamster
14
12
  class << self
15
13
  extend Forwardable
16
14
 
17
- # Create a list containing the given items
15
+ # Create a list containing the given items.
18
16
  #
19
17
  # @example
20
18
  # list = Hamster.list(:a, :b, :c)
21
19
  # # => [:a, :b, :c]
22
20
  #
23
- # @return [Hamster::List]
24
- #
25
- # @api public
21
+ # @return [List]
26
22
  def list(*items)
27
23
  items.to_list
28
24
  end
29
25
 
30
- # Create a lazy, infinite list
26
+ # Create a lazy, infinite list.
31
27
  #
32
- # The given block is repeatedly called to yield the elements of the list.
28
+ # The given block is called as necessary to return successive elements of the list.
33
29
  #
34
30
  # @example
35
31
  # Hamster.stream { :hello }.take(3)
36
32
  # # => [:hello, :hello, :hello]
37
33
  #
38
- # @return [Hamster::List]
39
- #
40
- # @api public
34
+ # @return [List]
41
35
  def stream(&block)
42
36
  return EmptyList unless block_given?
43
- Stream.new { Sequence.new(yield, stream(&block)) }
37
+ LazyList.new { Cons.new(yield, stream(&block)) }
44
38
  end
45
39
 
46
- # Construct a list of consecutive integers
40
+ # Construct a list of consecutive integers.
47
41
  #
48
42
  # @example
49
43
  # Hamster.interval(5,9)
@@ -51,9 +45,7 @@ module Hamster
51
45
  #
52
46
  # @param from [Integer] Start value, inclusive
53
47
  # @param to [Integer] End value, inclusive
54
- # @return [Hamster::List]
55
- #
56
- # @api public
48
+ # @return [List]
57
49
  def interval(from, to)
58
50
  return EmptyList if from > to
59
51
  interval_exclusive(from, to.next)
@@ -66,9 +58,9 @@ module Hamster
66
58
  # Hamster.repeat(:chunky).take(4)
67
59
  # => [:chunky, :chunky, :chunky, :chunky]
68
60
  #
69
- # @api public
61
+ # @return [List]
70
62
  def repeat(item)
71
- Stream.new { Sequence.new(item, repeat(item)) }
63
+ LazyList.new { Cons.new(item, repeat(item)) }
72
64
  end
73
65
 
74
66
  # Create a list that contains a given item a fixed number of times
@@ -77,43 +69,44 @@ module Hamster
77
69
  # Hamster.replicate(3).(:hamster)
78
70
  # #=> [:hamster, :hamster, :hamster]
79
71
  #
80
- # @api public
72
+ # @return [List]
81
73
  def replicate(number, item)
82
74
  repeat(item).take(number)
83
75
  end
84
76
 
85
- # Create an infinite list where each item is based on the previous one
77
+ # Create an infinite list where each item is derived from the previous one,
78
+ # using the provided block
86
79
  #
87
80
  # @example
88
- # Hamster.iterate(0) {|i| i.next}.take(5)
81
+ # Hamster.iterate(0) { |i| i.next }.take(5)
89
82
  # # => [0, 1, 2, 3, 4]
90
83
  #
91
84
  # @param item [Object] Starting value
92
85
  # @yieldparam [Object] The previous value
93
86
  # @yieldreturn [Object] The next value
94
- #
95
- # @api public
87
+ # @return [List]
96
88
  def iterate(item, &block)
97
- Stream.new { Sequence.new(item, iterate(yield(item), &block)) }
89
+ LazyList.new { Cons.new(item, iterate(yield(item), &block)) }
98
90
  end
99
91
 
100
- # Turn an enumerator into a Hamster list
92
+ # Turn an Enumerator into a `Hamster::List`. The result is a lazy collection
93
+ # where the values are memoized as they are generated.
101
94
  #
102
- # The result is a lazy collection where the values are memoized as they are
103
- # generated.
95
+ # If your code uses multiple threads, you need to make sure that the returned
96
+ # lazy collection is realized on a single thread only. Otherwise, a `FiberError`
97
+ # will be raised. After the collection is realized, it can be used from other
98
+ # threads as well.
104
99
  #
105
100
  # @example
106
- # def rg ; loop { yield rand(100) } ; end
101
+ # def rg; loop { yield rand(100) }; end
107
102
  # Hamster.enumerate(to_enum(:rg)).take(10)
108
103
  #
109
104
  # @param enum [Enumerator] The object to iterate over
110
- # @return [Stream]
111
- #
112
- # @api public
105
+ # @return [List]
113
106
  def enumerate(enum)
114
- Stream.new do
107
+ LazyList.new do
115
108
  begin
116
- Sequence.new(enum.next, enumerate(enum))
109
+ Cons.new(enum.next, enumerate(enum))
117
110
  rescue StopIteration
118
111
  EmptyList
119
112
  end
@@ -124,34 +117,37 @@ module Hamster
124
117
 
125
118
  def interval_exclusive(from, to)
126
119
  return EmptyList if from == to
127
- Stream.new { Sequence.new(from, interval_exclusive(from.next, to)) }
120
+ LazyList.new { Cons.new(from, interval_exclusive(from.next, to)) }
128
121
  end
129
122
  end
130
123
 
131
- # Common behavior for lists
132
- #
133
- # A +Hamster::List+ can be constructed with {Hamster.list Hamster.list}. It
134
- # consists of a +head+ (the first element) and a +tail+, containing the rest
135
- # of the list.
124
+ # A `List` can be constructed with {Hamster.list Hamster.list} or {List.[] List[]}.
125
+ # It consists of a *head* (the first element) and a *tail* (which itself is also
126
+ # a `List`, containing all the remaining elements).
136
127
  #
137
128
  # This is a singly linked list. Prepending to the list with {List#cons} runs
138
129
  # in constant time. Traversing the list from front to back is efficient,
139
- # indexed access however runs in linear time because the list needs to be
130
+ # however, indexed access runs in linear time because the list needs to be
140
131
  # traversed to find the element.
141
132
  #
142
- # In practice lists are constructed of a combination of {Sequence}, providing
143
- # the basic blocks that are linked, {Stream} for providing laziness, and
144
- # {EmptyList} as a terminator.
145
133
  module List
146
134
  extend Forwardable
147
135
  include Enumerable
148
- include Groupable
149
136
 
137
+ # @private
150
138
  CADR = /^c([ad]+)r$/
151
139
 
152
140
  def_delegator :self, :head, :first
153
141
  def_delegator :self, :empty?, :null?
154
142
 
143
+ # Create a new `List` populated with the given items.
144
+ # @return [Set]
145
+ def self.[](*items)
146
+ items.to_list
147
+ end
148
+
149
+ # Return the number of items in this `List`.
150
+ # @return [Integer]
155
151
  def size
156
152
  result, list = 0, self
157
153
  until list.empty?
@@ -166,20 +162,30 @@ module Hamster
166
162
  end
167
163
  def_delegator :self, :size, :length
168
164
 
165
+ # Create a new `List` with `item` added at the front.
166
+ # @param item [Object] The item to add
167
+ # @return [List]
169
168
  def cons(item)
170
- Sequence.new(item, self)
169
+ Cons.new(item, self)
171
170
  end
172
171
  def_delegator :self, :cons, :>>
173
172
  def_delegator :self, :cons, :conj
174
173
  def_delegator :self, :cons, :conjoin
175
174
 
175
+ # Create a new `List` with `item` added at the end.
176
+ # @param item [Object] The item to add
177
+ # @return [List]
176
178
  def add(item)
177
179
  append(Hamster.list(item))
178
180
  end
179
181
  def_delegator :self, :add, :<<
180
182
 
183
+ # Call the given block once for each item in the list, passing each
184
+ # item from first to last successively to the block.
185
+ #
186
+ # @return [self]
181
187
  def each
182
- return self unless block_given?
188
+ return to_enum unless block_given?
183
189
  list = self
184
190
  until list.empty?
185
191
  yield(list.head)
@@ -187,71 +193,103 @@ module Hamster
187
193
  end
188
194
  end
189
195
 
196
+ # Return a lazy list in which each element is derived from the corresponding
197
+ # element in this `List`, transformed through the given block.
198
+ #
199
+ # @return [List]
190
200
  def map(&block)
191
- return self unless block_given?
192
- Stream.new do
201
+ return enum_for(:map) unless block_given?
202
+ LazyList.new do
193
203
  next self if empty?
194
- Sequence.new(yield(head), tail.map(&block))
204
+ Cons.new(yield(head), tail.map(&block))
195
205
  end
196
206
  end
197
207
  def_delegator :self, :map, :collect
198
208
 
209
+ # Return a lazy list which is realized by transforming each item into a `List`,
210
+ # and flattening the resulting lists.
211
+ #
212
+ # @return [List]
199
213
  def flat_map(&block)
200
- return self unless block_given?
201
- Stream.new do
214
+ return enum_for(:flat_map) unless block_given?
215
+ LazyList.new do
202
216
  next self if empty?
203
217
  head_list = Hamster.list(*yield(head))
204
218
  next tail.flat_map(&block) if head_list.empty?
205
- Sequence.new(head_list.first, head_list.drop(1).append(tail.flat_map(&block)))
219
+ Cons.new(head_list.first, head_list.drop(1).append(tail.flat_map(&block)))
206
220
  end
207
221
  end
208
222
 
223
+ # Return a lazy list which contains all the items for which the given block
224
+ # returns true.
225
+ #
226
+ # @return [List]
209
227
  def filter(&block)
210
- return self unless block_given?
211
- Stream.new do
212
- next self if empty?
213
- next Sequence.new(head, tail.filter(&block)) if yield(head)
214
- tail.filter(&block)
228
+ return enum_for(:filter) unless block_given?
229
+ LazyList.new do
230
+ list = self
231
+ while true
232
+ break list if list.empty?
233
+ break Cons.new(list.head, list.tail.filter(&block)) if yield(list.head)
234
+ list = list.tail
235
+ end
215
236
  end
216
237
  end
217
238
 
239
+ # Return a lazy list which contains all elements up to, but not including, the
240
+ # first element for which the block returns `nil` or `false`.
241
+ #
242
+ # @return [List, Enumerator]
218
243
  def take_while(&block)
219
- return self unless block_given?
220
- Stream.new do
244
+ return enum_for(:take_while) unless block_given?
245
+ LazyList.new do
221
246
  next self if empty?
222
- next Sequence.new(head, tail.take_while(&block)) if yield(head)
247
+ next Cons.new(head, tail.take_while(&block)) if yield(head)
223
248
  EmptyList
224
249
  end
225
250
  end
226
251
 
252
+ # Return a lazy list which contains all elements starting from the
253
+ # first element for which the block returns `nil` or `false`.
254
+ #
255
+ # @return [List, Enumerator]
227
256
  def drop_while(&block)
228
- return self unless block_given?
229
- Stream.new do
257
+ return enum_for(:drop_while) unless block_given?
258
+ LazyList.new do
230
259
  list = self
231
260
  list = list.tail while !list.empty? && yield(list.head)
232
261
  list
233
262
  end
234
263
  end
235
264
 
265
+ # Return a lazy list containing the first `number` items from this `List`.
266
+ # @param number [Integer] The number of items to retain
267
+ # @return [List]
236
268
  def take(number)
237
- Stream.new do
269
+ LazyList.new do
238
270
  next self if empty?
239
- next Sequence.new(head, tail.take(number - 1)) if number > 0
271
+ next Cons.new(head, tail.take(number - 1)) if number > 0
240
272
  EmptyList
241
273
  end
242
274
  end
243
275
 
276
+ # Return a lazy list containing all but the last item from this `List`.
277
+ # @return [List]
244
278
  def pop
245
- Stream.new do
279
+ LazyList.new do
246
280
  next self if empty?
247
281
  new_size = size - 1
248
- next Sequence.new(head, tail.take(new_size - 1)) if new_size >= 1
282
+ next Cons.new(head, tail.take(new_size - 1)) if new_size >= 1
249
283
  EmptyList
250
284
  end
251
285
  end
252
286
 
287
+ # Return a lazy list containing all items after the first `number` items from
288
+ # this `List`.
289
+ # @param number [Integer] The number of items to skip over
290
+ # @return [List]
253
291
  def drop(number)
254
- Stream.new do
292
+ LazyList.new do
255
293
  list = self
256
294
  while !list.empty? && number > 0
257
295
  number -= 1
@@ -261,221 +299,608 @@ module Hamster
261
299
  end
262
300
  end
263
301
 
302
+ # Return a lazy list with all items from this `List`, followed by all items from
303
+ # `other`.
304
+ #
305
+ # @param other [List] The list to add onto the end of this one
306
+ # @return [List]
264
307
  def append(other)
265
- Stream.new do
308
+ LazyList.new do
266
309
  next other if empty?
267
- Sequence.new(head, tail.append(other))
310
+ Cons.new(head, tail.append(other))
268
311
  end
269
312
  end
270
313
  def_delegator :self, :append, :concat
271
314
  def_delegator :self, :append, :cat
272
315
  def_delegator :self, :append, :+
273
316
 
317
+ # Return a `List` with the same items, but in reverse order.
318
+ # @return [List]
274
319
  def reverse
275
- Stream.new { reduce(EmptyList) { |list, item| list.cons(item) } }
320
+ LazyList.new { reduce(EmptyList) { |list, item| list.cons(item) }}
276
321
  end
277
322
 
278
- def zip(other)
279
- Stream.new do
280
- next self if empty? && other.empty?
281
- Sequence.new(Sequence.new(head, Sequence.new(other.head)), tail.zip(other.tail))
323
+ # Gather the corresponding elements from this `List` and `others` (that is,
324
+ # the elements with the same indices) into new 2-element lists. Return a
325
+ # lazy list of these 2-element lists.
326
+ #
327
+ # @param others [List] A list of the lists to zip together with this one
328
+ # @return [List]
329
+ def zip(others)
330
+ LazyList.new do
331
+ next self if empty? && others.empty?
332
+ Cons.new(Cons.new(head, Cons.new(others.head)), tail.zip(others.tail))
282
333
  end
283
334
  end
284
335
 
336
+ # Gather the first element of each nested list into a new `List`, then the second
337
+ # element of each nested list, then the third, and so on. In other words, if each
338
+ # nested list is a "row", return a lazy list of "columns" instead.
339
+ #
340
+ # Although the returned list is lazy, each returned nested list (each "column")
341
+ # is strict. So while each nested list in the input can be infinite, the parent
342
+ # `List` must not be, or trying to realize the first element in the output will
343
+ # cause an infinite loop.
344
+ #
345
+ # @example
346
+ # # First let's create some infinite lists
347
+ # list1 = Hamster.iterate(1, &:next)
348
+ # list2 = Hamster.iterate(2) { |n| n * 2 }
349
+ # list3 = Hamster.iterate(3) { |n| n * 3 }
350
+ #
351
+ # # Now we transpose our 3 infinite "rows" into an infinite series of 3-element "columns"
352
+ # Hamster.list(list1, list2, list3).transpose.take(4)
353
+ # # => Hamster::List[
354
+ # # Hamster::List[1, 2, 3],
355
+ # # Hamster::List[2, 4, 9],
356
+ # # Hamster::List[3, 8, 27],
357
+ # # Hamster::List[4, 16, 81]]
358
+ #
359
+ # @return [List]
360
+ def transpose
361
+ return EmptyList if empty?
362
+ LazyList.new do
363
+ next EmptyList if any? { |list| list.empty? }
364
+ heads, tails = EmptyList, EmptyList
365
+ reverse_each { |list| heads, tails = heads.cons(list.head), tails.cons(list.tail) }
366
+ Cons.new(heads, tails.transpose)
367
+ end
368
+ end
369
+
370
+ # Concatenate an infinite series of copies of this `List` together (into a
371
+ # new lazy list). Or, if empty, just return an empty list.
372
+ #
373
+ # @return [List]
285
374
  def cycle
286
- Stream.new do
375
+ LazyList.new do
287
376
  next self if empty?
288
- Sequence.new(head, tail.append(cycle))
377
+ Cons.new(head, tail.append(cycle))
289
378
  end
290
379
  end
291
380
 
381
+ # Return a new `List` with the same elements, but rotated so that the one at
382
+ # index `count` is the first element of the new list. If `count` is positive,
383
+ # the elements will be shifted left, and those shifted past the lowest position
384
+ # will be moved to the end. If `count` is negative, the elements will be shifted
385
+ # right, and those shifted past the last position will be moved to the beginning.
386
+ #
387
+ # @param count [Integer] The number of positions to shift items by
388
+ # @return [Vector]
389
+ def rotate(count = 1)
390
+ raise TypeError, "expected Integer" if not count.is_a?(Integer)
391
+ return self if empty? || (count % size) == 0
392
+ count = (count >= 0) ? count % size : (size - (~count % size) - 1)
393
+ drop(count).append(take(count))
394
+ end
395
+
396
+ # Return 2 `List`s, one of the first `number` items, and another of all the
397
+ # remaining items.
398
+ # @param number [Integer] The index at which to split this list
399
+ # @return [Array]
292
400
  def split_at(number)
293
- Tuple.new(take(number), drop(number))
401
+ [take(number), drop(number)].freeze
294
402
  end
295
403
 
404
+ # Return 2 `List`s, one up to (but not including) the first item for which the
405
+ # block returns `nil` or `false`, and another of all the remaining items.
406
+ #
407
+ # @return [Array]
296
408
  def span(&block)
297
- return Tuple.new(self, EmptyList) unless block_given?
298
- Tuple.new(take_while(&block), drop_while(&block))
409
+ return [self, EmptyList].freeze unless block_given?
410
+ splitter = Splitter.new(self, block)
411
+ mutex = Mutex.new
412
+ [Splitter::Left.new(splitter, splitter.left, mutex),
413
+ Splitter::Right.new(splitter, mutex)].freeze
299
414
  end
300
415
 
416
+ # Return 2 `List`s, one up to (but not including) the first item for which the
417
+ # block returns true, and another of all the remaining items.
418
+ #
419
+ # @return [Array]
301
420
  def break(&block)
302
421
  return span unless block_given?
303
422
  span { |item| !yield(item) }
304
423
  end
305
424
 
425
+ # Return an empty `List`.
426
+ # @return [List]
306
427
  def clear
307
428
  EmptyList
308
429
  end
309
430
 
431
+ # Return a `List` with the same items, but sorted either in their natural order,
432
+ # or using an optional comparator block. The block must take 2 parameters, and
433
+ # return 0, 1, or -1 if the first one is equal, greater than, or less than the
434
+ # second (respectively).
435
+ #
436
+ # @return [List]
310
437
  def sort(&comparator)
311
- Stream.new { Sorter.new(self).sort(&comparator) }
438
+ LazyList.new { super(&comparator).to_list }
312
439
  end
313
440
 
441
+ # Return a new `List` with the same items, but sorted. The sort order will be
442
+ # determined by mapping the items through the given block to obtain sort keys,
443
+ # and then sorting the keys according to their natural sort order.
444
+ #
445
+ # @return [List]
314
446
  def sort_by(&transformer)
315
447
  return sort unless block_given?
316
- Stream.new { Sorter.new(self).sort_by(&transformer) }
317
- end
318
-
319
- def join(sep = "")
320
- return "" if empty?
321
- sep = sep.to_s
322
- tail.reduce(head.to_s.dup) { |result, item| result << sep << item.to_s }
448
+ LazyList.new { super(&transformer).to_list }
323
449
  end
324
450
 
451
+ # Return a new `List` with `sep` inserted between each of the existing elements.
452
+ #
453
+ # @example
454
+ # Hamster.list('one', 'two', 'three').intersperse(' ')
455
+ # # => Hamster::List['one', ' ', 'two', ' ', 'three']
456
+ #
457
+ # @return [List]
325
458
  def intersperse(sep)
326
- Stream.new do
459
+ LazyList.new do
327
460
  next self if tail.empty?
328
- Sequence.new(head, Sequence.new(sep, tail.intersperse(sep)))
461
+ Cons.new(head, Cons.new(sep, tail.intersperse(sep)))
329
462
  end
330
463
  end
331
464
 
332
- def uniq(items = EmptySet)
333
- Stream.new do
465
+ # Return a lazy list with the same items, but all duplicates removed.
466
+ # Use `#hash` and `#eql?` to determine which items are duplicates.
467
+ #
468
+ # @return [List]
469
+ def uniq(items = ::Set.new)
470
+ LazyList.new do
334
471
  next self if empty?
335
472
  next tail.uniq(items) if items.include?(head)
336
- Sequence.new(head, tail.uniq(items.add(head)))
473
+ Cons.new(head, tail.uniq(items.add(head)))
337
474
  end
338
475
  end
339
476
  def_delegator :self, :uniq, :nub
340
477
  def_delegator :self, :uniq, :remove_duplicates
341
478
 
342
- def union(other)
343
- append(other).uniq
479
+ # Return a `List` with all the elements from both this list and `other`,
480
+ # with all duplicates removed.
481
+ #
482
+ # @param other [List] The list to merge with
483
+ # @return [List]
484
+ def union(other, items = ::Set.new)
485
+ LazyList.new do
486
+ next other.uniq(items) if empty?
487
+ next tail.union(other, items) if items.include?(head)
488
+ Cons.new(head, tail.union(other, items.add(head)))
489
+ end
344
490
  end
345
491
  def_delegator :self, :union, :|
346
492
 
493
+ # Return a lazy list with all elements except the last one.
494
+ # @return [List]
347
495
  def init
348
496
  return EmptyList if tail.empty?
349
- Stream.new { Sequence.new(head, tail.init) }
497
+ LazyList.new { Cons.new(head, tail.init) }
350
498
  end
351
499
 
500
+ # Return the last item in this list.
501
+ # @return [Object]
352
502
  def last
353
503
  list = self
354
504
  list = list.tail until list.tail.empty?
355
505
  list.head
356
506
  end
357
507
 
508
+ # Return a lazy list of all suffixes of this list.
509
+ #
510
+ # @example
511
+ # Hamster.list(1,2,3).tails
512
+ # # => Hamster::List[
513
+ # # Hamster::List[1, 2, 3],
514
+ # # Hamster::List[2, 3],
515
+ # # Hamster::List[3]]
516
+ #
517
+ # @return [List]
358
518
  def tails
359
- Stream.new do
360
- next Sequence.new(self) if empty?
361
- Sequence.new(self, tail.tails)
519
+ LazyList.new do
520
+ next self if empty?
521
+ Cons.new(self, tail.tails)
362
522
  end
363
523
  end
364
524
 
525
+ # Return a lazy list of all prefixes of this list.
526
+ #
527
+ # @example
528
+ # Hamster.list(1,2,3).inits
529
+ # # => Hamster::List[
530
+ # # Hamster::List[1],
531
+ # # Hamster::List[1, 2],
532
+ # # Hamster::List[1, 2, 3]]
533
+ #
534
+ # @return [List]
365
535
  def inits
366
- Stream.new do
367
- next Sequence.new(self) if empty?
368
- Sequence.new(EmptyList, tail.inits.map { |list| list.cons(head) })
536
+ LazyList.new do
537
+ next self if empty?
538
+ Cons.new(Hamster.list(head), tail.inits.map { |list| list.cons(head) })
369
539
  end
370
540
  end
371
541
 
372
- def combinations(number)
373
- return Sequence.new(EmptyList) if number == 0
374
- Stream.new do
542
+ # Return a lazy list of all combinations of length `n` of items from this `List`.
543
+ #
544
+ # @example
545
+ # Hamster.list(1,2,3).combination(2)
546
+ # # => Hamster::List[
547
+ # # Hamster::List[1, 2],
548
+ # # Hamster::List[1, 3],
549
+ # # Hamster::List[2, 3]]
550
+ #
551
+ # @return [List]
552
+ def combination(n)
553
+ return Cons.new(EmptyList) if n == 0
554
+ LazyList.new do
375
555
  next self if empty?
376
- tail.combinations(number - 1).map { |list| list.cons(head) }.append(tail.combinations(number))
556
+ tail.combination(n - 1).map { |list| list.cons(head) }.append(tail.combination(n))
377
557
  end
378
558
  end
379
- def_delegator :self, :combinations, :combination
380
559
 
560
+ # Split the items in this list in groups of `number`. Return a list of lists.
561
+ # @return [List]
381
562
  def chunk(number)
382
- Stream.new do
563
+ LazyList.new do
383
564
  next self if empty?
384
565
  first, remainder = split_at(number)
385
- Sequence.new(first, remainder.chunk(number))
566
+ Cons.new(first, remainder.chunk(number))
386
567
  end
387
568
  end
388
569
 
570
+ # Split the items in this list in groups of `number`, and yield each group
571
+ # to the block (as a `List`).
572
+ # @return [self]
389
573
  def each_chunk(number, &block)
574
+ return enum_for(:each_chunk, number) unless block_given?
390
575
  chunk(number).each(&block)
576
+ self
391
577
  end
392
578
  def_delegator :self, :each_chunk, :each_slice
393
579
 
580
+ # Return a new `List` with all nested lists recursively "flattened out",
581
+ # that is, their elements inserted into the new `List` in the place where
582
+ # the nested list originally was.
583
+ #
584
+ # @return [List]
394
585
  def flatten
395
- Stream.new do
586
+ LazyList.new do
396
587
  next self if empty?
397
588
  next head.append(tail.flatten) if head.is_a?(List)
398
- Sequence.new(head, tail.flatten)
589
+ Cons.new(head, tail.flatten)
399
590
  end
400
591
  end
401
592
 
593
+ # Passes each item to the block, and gathers them into a {Hash} where the
594
+ # keys are return values from the block, and the values are `List`s of items
595
+ # for which the block returned that value.
596
+ #
597
+ # @return [Hash]
402
598
  def group_by(&block)
403
599
  group_by_with(EmptyList, &block)
404
600
  end
405
601
  def_delegator :self, :group_by, :group
406
602
 
603
+ # Retrieve the item at `index`. Negative indices count back from the end of
604
+ # the list (-1 is the last item). If `index` is invalid (either too high or
605
+ # too low), return `nil`.
606
+ #
607
+ # @param index [Integer] The index to retrieve
608
+ # @return [Object]
407
609
  def at(index)
408
- drop(index).head
409
- end
410
-
411
- def slice(from, length = Undefined)
412
- return at(from) if length.equal?(Undefined)
413
- drop(from).take(length)
414
- end
415
- def_delegator :self, :slice, :[]
416
-
417
- def find_index
418
- return nil unless block_given?
419
- i = 0
420
- list = self
421
- loop do
422
- return nil if list.empty?
423
- return i if yield(list.head)
424
- i += 1
425
- list = list.tail
610
+ index += size if index < 0
611
+ return nil if index < 0
612
+ node = self
613
+ while index > 0
614
+ node = node.tail
615
+ index -= 1
426
616
  end
617
+ node.head
427
618
  end
428
619
 
429
- def elem_index(object)
430
- find_index { |item| item == object }
431
- end
432
-
433
- def index(object = Undefined, &block)
434
- return elem_index(object) unless object.equal?(Undefined)
435
- find_index(&block)
620
+ # Element reference. Return the item at a specific index, or a specified,
621
+ # contiguous range of items (as a new list).
622
+ #
623
+ # @overload list[index]
624
+ # Return the item at `index`.
625
+ # @param index [Integer] The index to retrieve.
626
+ # @overload list[start, length]
627
+ # Return a sublist starting at index `start` and continuing for `length` elements.
628
+ # @param start [Integer] The index to start retrieving items from.
629
+ # @param length [Integer] The number of items to retrieve.
630
+ # @overload list[range]
631
+ # Return a sublist specified by the given `range` of indices.
632
+ # @param range [Range] The range of indices to retrieve.
633
+ #
634
+ # @return [Object]
635
+ def [](arg, length = (missing_length = true))
636
+ if missing_length
637
+ if arg.is_a?(Range)
638
+ from, to = arg.begin, arg.end
639
+ from += size if from < 0
640
+ return nil if from < 0
641
+ to += size if to < 0
642
+ to += 1 if !arg.exclude_end?
643
+ length = to - from
644
+ length = 0 if length < 0
645
+ list = self
646
+ while from > 0
647
+ return nil if list.empty?
648
+ list = list.tail
649
+ from -= 1
650
+ end
651
+ list.take(length)
652
+ else
653
+ at(arg)
654
+ end
655
+ else
656
+ return nil if length < 0
657
+ arg += size if arg < 0
658
+ return nil if arg < 0
659
+ list = self
660
+ while arg > 0
661
+ return nil if list.empty?
662
+ list = list.tail
663
+ arg -= 1
664
+ end
665
+ list.take(length)
666
+ end
436
667
  end
668
+ def_delegator :self, :[], :slice
437
669
 
670
+ # Pass each item successively to the block, and return a `List` of indices where
671
+ # the block returns true.
672
+ #
673
+ # @return [List]
438
674
  def find_indices(i = 0, &block)
439
- return EmptyList unless block_given?
440
- Stream.new do
441
- next EmptyList if empty?
442
- next Sequence.new(i, tail.find_indices(i + 1, &block)) if yield(head)
443
- tail.find_indices(i + 1, &block)
675
+ return EmptyList if empty? || !block_given?
676
+ LazyList.new do
677
+ node = self
678
+ while true
679
+ break Cons.new(i, node.tail.find_indices(i + 1, &block)) if yield(node.head)
680
+ node = node.tail
681
+ break EmptyList if node.empty?
682
+ i += 1
683
+ end
444
684
  end
445
685
  end
446
686
 
687
+ # Return a `List` of indices where `object` is found. Use `#==` for testing equality.
688
+ #
689
+ # @param object [Object] The object to search for
690
+ # @return [List]
447
691
  def elem_indices(object)
448
692
  find_indices { |item| item == object }
449
693
  end
450
694
 
695
+ # Return a `List` of indices where the given object is found, or where the given
696
+ # block returns true.
697
+ #
698
+ # @overload indices(obj)
699
+ # Return a `List` of indices where `obj` is found. Use `#==` for testing equality.
700
+ # @overload indices { |item| ... }
701
+ # Pass each item successively to the block. Return a list of indices where the
702
+ # block returns true.
703
+ #
704
+ # @return [List]
451
705
  def indices(object = Undefined, &block)
452
706
  return elem_indices(object) unless object.equal?(Undefined)
453
707
  find_indices(&block)
454
708
  end
455
709
 
710
+ # Merge all the nested lists into a single list, using the given comparator
711
+ # block to determine the order which items should be shifted out of the nested
712
+ # lists and into the output list. The comparator should take 2 parameters and
713
+ # return 0, 1, or -1 if the first parameter is (respectively) equal to, greater
714
+ # than, or less than the second parameter. Whichever nested list's `#head` is
715
+ # determined to be "lowest" according to the comparator will be the first in
716
+ # the merged `List`.
717
+ #
718
+ # @return [List]
456
719
  def merge(&comparator)
457
720
  return merge_by unless block_given?
458
- Stream.new do
721
+ LazyList.new do
459
722
  sorted = remove(&:empty?).sort do |a, b|
460
723
  yield(a.head, b.head)
461
724
  end
462
725
  next EmptyList if sorted.empty?
463
- Sequence.new(sorted.head.head, sorted.tail.cons(sorted.head.tail).merge(&comparator))
726
+ Cons.new(sorted.head.head, sorted.tail.cons(sorted.head.tail).merge(&comparator))
464
727
  end
465
728
  end
466
729
 
730
+ # Merge all the nested lists into a single list, using sort keys generated
731
+ # by mapping the items in the nested lists through the given block to determine the
732
+ # order which items should be shifted out of the nested lists and into the output
733
+ # list. Whichever nested list's `#head` has the "lowest" sort key (according to
734
+ # their natural order) will be the first in the merged `List`.
735
+ #
736
+ # @return [List]
467
737
  def merge_by(&transformer)
468
738
  return merge_by { |item| item } unless block_given?
469
- Stream.new do
739
+ LazyList.new do
470
740
  sorted = remove(&:empty?).sort_by do |list|
471
741
  yield(list.head)
472
742
  end
473
743
  next EmptyList if sorted.empty?
474
- Sequence.new(sorted.head.head, sorted.tail.cons(sorted.head.tail).merge_by(&transformer))
744
+ Cons.new(sorted.head.head, sorted.tail.cons(sorted.head.tail).merge_by(&transformer))
745
+ end
746
+ end
747
+
748
+ # Return a randomly chosen element from this list.
749
+ # @return [Object]
750
+ def sample
751
+ at(rand(size))
752
+ end
753
+
754
+ # Return a new `List` with the given items inserted before the item at `index`.
755
+ #
756
+ # @param index [Integer] The index where the new items should go
757
+ # @param items [Array] The items to add
758
+ # @return [List]
759
+ def insert(index, *items)
760
+ if index == 0
761
+ return items.to_list.append(self)
762
+ elsif index > 0
763
+ LazyList.new do
764
+ Cons.new(head, tail.insert(index-1, *items))
765
+ end
766
+ else
767
+ raise IndexError if index < -size
768
+ insert(index + size, *items)
769
+ end
770
+ end
771
+
772
+ # Return a lazy list with all elements equal to `obj` removed. `#==` is used
773
+ # for testing equality.
774
+ # @param obj [Object] The object to remove.
775
+ # @return [List]
776
+ def delete(obj)
777
+ list = self
778
+ list = list.tail while list.head == obj && !list.empty?
779
+ return EmptyList if list.empty?
780
+ LazyList.new { Cons.new(list.head, list.tail.delete(obj)) }
781
+ end
782
+
783
+ # Return a lazy list containing the same items, minus the one at `index`.
784
+ # If `index` is negative, it counts back from the end of the list.
785
+ #
786
+ # @param index [Integer] The index of the item to remove
787
+ # @return [List]
788
+ def delete_at(index)
789
+ if index == 0
790
+ tail
791
+ elsif index < 0
792
+ index += size if index < 0
793
+ return self if index < 0
794
+ delete_at(index)
795
+ else
796
+ LazyList.new { Cons.new(head, tail.delete_at(index - 1)) }
797
+ end
798
+ end
799
+
800
+ # Replace a range of indexes with the given object.
801
+ #
802
+ # @overload fill(obj)
803
+ # Return a new `List` of the same size, with every item set to `obj`.
804
+ # @overload fill(obj, start)
805
+ # Return a new `List` with all indexes from `start` to the end of the
806
+ # list set to `obj`.
807
+ # @overload fill(obj, start, length)
808
+ # Return a new `List` with `length` indexes, beginning from `start`,
809
+ # set to `obj`.
810
+ #
811
+ # @return [List]
812
+ def fill(obj, index = 0, length = nil)
813
+ if index == 0
814
+ length ||= size
815
+ if length > 0
816
+ LazyList.new do
817
+ Cons.new(obj, tail.fill(obj, 0, length-1))
818
+ end
819
+ else
820
+ self
821
+ end
822
+ elsif index > 0
823
+ LazyList.new do
824
+ Cons.new(head, tail.fill(obj, index-1, length))
825
+ end
826
+ else
827
+ raise IndexError if index < -size
828
+ fill(obj, index + size, length)
829
+ end
830
+ end
831
+
832
+ # Yields all permutations of length `n` of the items in the list, and then
833
+ # returns `self`. If no length `n` is specified, permutations of the entire
834
+ # list will be yielded.
835
+ #
836
+ # There is no guarantee about which order the permutations will be yielded in.
837
+ #
838
+ # If no block is given, an `Enumerator` is returned instead.
839
+ #
840
+ # @return [self, Enumerator]
841
+ def permutation(length = size, &block)
842
+ return enum_for(:permutation, length) if not block_given?
843
+ if length == 0
844
+ yield EmptyList
845
+ elsif length == 1
846
+ each { |obj| yield Cons.new(obj, EmptyList) }
847
+ elsif not empty?
848
+ if length < size
849
+ tail.permutation(length, &block)
850
+ end
851
+
852
+ tail.permutation(length-1) do |p|
853
+ 0.upto(length-1) do |i|
854
+ left,right = p.split_at(i)
855
+ yield left.append(right.cons(head))
856
+ end
857
+ end
475
858
  end
859
+ self
476
860
  end
477
861
 
478
- # Value-and-type equality
862
+ # Yield every non-empty sublist to the given block. (The entire `List` also
863
+ # counts as one sublist.)
864
+ #
865
+ # @example
866
+ # Hamster.list(1, 2, 3).subsequences { |list| p list }
867
+ # # prints:
868
+ # # Hamster::List[1]
869
+ # # Hamster::List[1, 2]
870
+ # # Hamster::List[1, 2, 3]
871
+ # # Hamster::List[2]
872
+ # # Hamster::List[2, 3]
873
+ # # Hamster::List[3]
874
+ #
875
+ # @yield [sublist] One or more contiguous elements from this list
876
+ # @return [self]
877
+ def subsequences(&block)
878
+ return enum_for(:subsequences) if not block_given?
879
+ if not empty?
880
+ 1.upto(size) do |n|
881
+ yield take(n)
882
+ end
883
+ tail.subsequences(&block)
884
+ end
885
+ self
886
+ end
887
+
888
+ # Return 2 `List`s, the first containing all the elements for which the block
889
+ # evaluates to true, the second containing the rest.
890
+ #
891
+ # @return [List]
892
+ def partition(&block)
893
+ return enum_for(:partition) if not block_given?
894
+ partitioner = Partitioner.new(self, block)
895
+ mutex = Mutex.new
896
+ [Partitioned.new(partitioner, partitioner.left, mutex),
897
+ Partitioned.new(partitioner, partitioner.right, mutex)].freeze
898
+ end
899
+
900
+ # Return true if `other` has the same type and contents as this `Hash`.
901
+ #
902
+ # @param other [Object] The collection to compare with
903
+ # @return [Boolean]
479
904
  def eql?(other)
480
905
  list = self
481
906
  loop do
@@ -489,37 +914,57 @@ module Hamster
489
914
  end
490
915
  end
491
916
 
492
- # Value equality, will do type coercion on arrays and array-like objects
493
- def ==(other)
494
- self.eql?(other) ||
495
- other.respond_to?(:to_ary) && to_ary.eql?(other.to_ary)
496
- end
497
-
917
+ # See `Object#hash`
918
+ # @return [Integer]
498
919
  def hash
499
920
  reduce(0) { |hash, item| (hash << 5) - hash + item.hash }
500
921
  end
501
922
 
923
+ # Return `self`.
924
+ # @return [List]
502
925
  def dup
503
926
  self
504
927
  end
505
928
  def_delegator :self, :dup, :clone
506
929
 
930
+ # Return `self`.
931
+ # @return [List]
507
932
  def to_list
508
933
  self
509
934
  end
510
935
 
511
- def to_set
512
- reduce(EmptySet) { |set, item| set.add(item) }
936
+ # Return the contents of this `List` as a programmer-readable `String`. If all the
937
+ # items in the list are serializable as Ruby literal strings, the returned string can
938
+ # be passed to `eval` to reconstitute an equivalent `List`.
939
+ #
940
+ # @return [String]
941
+ def inspect
942
+ result = "Hamster::List["
943
+ each_with_index { |obj, i| result << ', ' if i > 0; result << obj.inspect }
944
+ result << "]"
513
945
  end
514
946
 
515
- def inspect
516
- to_a.inspect
947
+ # Allows this `List` to be printed at the `pry` console, or using `pp` (from the
948
+ # Ruby standard library), in a way which takes the amount of horizontal space on
949
+ # the screen into account, and which indents nested structures to make them easier
950
+ # to read.
951
+ #
952
+ # @private
953
+ def pretty_print(pp)
954
+ pp.group(1, "Hamster::List[", "]") do
955
+ pp.breakable ''
956
+ pp.seplist(self) { |obj| obj.pretty_print(pp) }
957
+ end
517
958
  end
518
959
 
960
+ # @private
519
961
  def respond_to?(name, include_private = false)
520
962
  super || !!name.to_s.match(CADR)
521
963
  end
522
964
 
965
+ # Return `true` if the size of this list can be obtained in constant time (without
966
+ # traversing the list).
967
+ # @return [Integer]
523
968
  def cached_size?
524
969
  false
525
970
  end
@@ -548,15 +993,16 @@ module Hamster
548
993
 
549
994
  # The basic building block for constructing lists
550
995
  #
551
- # A Sequence, also known as a "cons cell", has a +head+ and a +tail+, where
552
- # the +head+ is an element in the list, and the +tail+ is a reference to the
996
+ # A Cons, also known as a "cons cell", has a "head" and a "tail", where
997
+ # the head is an element in the list, and the tail is a reference to the
553
998
  # rest of the list. This way a singly linked list can be constructed, with
554
- # each +Sequence+ holding a single element and a pointer to the next
555
- # +Sequence+.
999
+ # each `Cons` holding a single element and a pointer to the next
1000
+ # `Cons`.
556
1001
  #
557
- # The last +Sequence+ instance in the chain has the {EmptyList} as its tail.
1002
+ # The last `Cons` instance in the chain has the {EmptyList} as its tail.
558
1003
  #
559
- class Sequence
1004
+ # @private
1005
+ class Cons
560
1006
  include List
561
1007
 
562
1008
  attr_reader :head, :tail
@@ -572,7 +1018,7 @@ module Hamster
572
1018
  end
573
1019
 
574
1020
  def size
575
- @size || super
1021
+ @size ||= super
576
1022
  end
577
1023
 
578
1024
  def cached_size?
@@ -580,31 +1026,38 @@ module Hamster
580
1026
  end
581
1027
  end
582
1028
 
583
- # Lazy list stream
584
- #
585
- # A +Stream+ takes a block that returns a +List+, i.e. an object that responds
586
- # to +head+, +tail+ and +empty?+. The list is only realized when one of these
587
- # operations is performed.
1029
+ # A `LazyList` takes a block that returns a `List`, i.e. an object that responds
1030
+ # to `#head`, `#tail` and `#empty?`. The list is only realized (i.e. the block is
1031
+ # only called) when one of these operations is performed.
588
1032
  #
589
- # By returning a +Sequence+ that in turn has a {Stream} as its +tail+, one can
1033
+ # By returning a `Cons` that in turn has a {LazyList} as its tail, one can
590
1034
  # construct infinite lazy lists.
591
1035
  #
592
- # The recommended interface for using this is through {Hamster.stream Hamster.stream}
593
- #
594
- class Stream
595
- extend Forwardable
596
-
1036
+ # @private
1037
+ class LazyList
597
1038
  include List
598
1039
 
599
1040
  def initialize(&block)
600
- @block = block
601
- @lock = Mutex.new
602
- @size = nil
1041
+ @head = block # doubles as storage for block while yet unrealized
1042
+ @tail = nil
1043
+ @atomic = Atomic.new(0) # haven't yet run block
1044
+ @size = nil
1045
+ end
1046
+
1047
+ def head
1048
+ realize if @atomic.get != 2
1049
+ @head
1050
+ end
1051
+
1052
+ def tail
1053
+ realize if @atomic.get != 2
1054
+ @tail
603
1055
  end
604
1056
 
605
- def_delegator :target, :head
606
- def_delegator :target, :tail
607
- def_delegator :target, :empty?
1057
+ def empty?
1058
+ realize if @atomic.get != 2
1059
+ @size == 0
1060
+ end
608
1061
 
609
1062
  def size
610
1063
  @size ||= super
@@ -614,39 +1067,227 @@ module Hamster
614
1067
  @size != nil
615
1068
  end
616
1069
 
617
- protected
1070
+ private
618
1071
 
619
- def vivify
620
- @lock.synchronize do
621
- unless @block.nil?
622
- @target = @block.call
623
- @block = nil
1072
+ QUEUE = ConditionVariable.new
1073
+ MUTEX = Mutex.new
1074
+
1075
+ def realize
1076
+ while true
1077
+ # try to "claim" the right to run the block which realizes target
1078
+ if @atomic.compare_and_swap(0,1) # full memory barrier here
1079
+ begin
1080
+ list = @head.call
1081
+ if list.empty?
1082
+ @head, @tail, @size = nil, self, 0
1083
+ else
1084
+ @head, @tail = list.head, list.tail
1085
+ end
1086
+ rescue
1087
+ @atomic.set(0)
1088
+ MUTEX.synchronize { QUEUE.broadcast }
1089
+ raise
1090
+ end
1091
+ @atomic.set(2)
1092
+ MUTEX.synchronize { QUEUE.broadcast }
1093
+ return
1094
+ end
1095
+ # we failed to "claim" it, another thread must be running it
1096
+ if @atomic.get == 1 # another thread is running the block
1097
+ MUTEX.synchronize do
1098
+ # check value of @atomic again, in case another thread already changed it
1099
+ # *and* went past the call to QUEUE.broadcast before we got here
1100
+ QUEUE.wait(MUTEX) if @atomic.get == 1
1101
+ end
1102
+ elsif @atomic.get == 2 # another thread finished the block
1103
+ return
624
1104
  end
625
1105
  end
626
- @target
627
1106
  end
1107
+ end
628
1108
 
629
- private
1109
+ # Common behavior for other classes which implement various kinds of lazy lists
1110
+ # @private
1111
+ class Realizable
1112
+ include List
1113
+
1114
+ def initialize
1115
+ @head, @tail, @size = Undefined, Undefined, nil
1116
+ end
1117
+
1118
+ def head
1119
+ realize if @head == Undefined
1120
+ @head
1121
+ end
1122
+
1123
+ def tail
1124
+ realize if @tail == Undefined
1125
+ @tail
1126
+ end
1127
+
1128
+ def empty?
1129
+ realize if @head == Undefined
1130
+ @size == 0
1131
+ end
1132
+
1133
+ def size
1134
+ @size ||= super
1135
+ end
630
1136
 
631
- def target
632
- list = vivify
633
- list = list.vivify while list.is_a?(Stream)
634
- list
1137
+ def cached_size?
1138
+ @size != nil
1139
+ end
1140
+
1141
+ def realized?
1142
+ @head != Undefined
1143
+ end
1144
+ end
1145
+
1146
+ # This class can divide a collection into 2 lazy lists, one of items
1147
+ # for which the block returns true, and another for false
1148
+ # At the same time, it guarantees the block will only be called ONCE for each item
1149
+ #
1150
+ # @private
1151
+ class Partitioner
1152
+ attr_reader :left, :right
1153
+ def initialize(list, block)
1154
+ @list, @block, @left, @right = list, block, [], []
1155
+ end
1156
+
1157
+ def next_item
1158
+ unless @list.empty?
1159
+ item = @list.head
1160
+ (@block.call(item) ? @left : @right) << item
1161
+ @list = @list.tail
1162
+ end
1163
+ end
1164
+
1165
+ def done?
1166
+ @list.empty?
1167
+ end
1168
+ end
1169
+
1170
+ # One of the lazy lists which gets its items from a Partitioner
1171
+ # @private
1172
+ class Partitioned < Realizable
1173
+ def initialize(partitioner, buffer, mutex)
1174
+ super()
1175
+ @partitioner, @buffer, @mutex = partitioner, buffer, mutex
1176
+ end
1177
+
1178
+ def realize
1179
+ @mutex.synchronize do
1180
+ return if @head != Undefined # another thread got ahead of us
1181
+ while true
1182
+ if !@buffer.empty?
1183
+ @head = @buffer.shift
1184
+ @tail = Partitioned.new(@partitioner, @buffer, @mutex)
1185
+ # don't hold onto references
1186
+ # tail will keep references alive until end of list is reached
1187
+ @partitioner, @buffer, @mutex = nil, nil, nil
1188
+ return
1189
+ elsif @partitioner.done?
1190
+ @head, @size, @tail = nil, 0, self
1191
+ @partitioner, @buffer, @mutex = nil, nil, nil # allow them to be GC'd
1192
+ return
1193
+ else
1194
+ @partitioner.next_item
1195
+ end
1196
+ end
1197
+ end
1198
+ end
1199
+ end
1200
+
1201
+ # This class can divide a list up into 2 lazy lists, one for the prefix of elements
1202
+ # for which the block returns true, and another for all the elements after that
1203
+ # It guarantees that the block will only be called ONCE for each item
1204
+ #
1205
+ # @private
1206
+ class Splitter
1207
+ attr_reader :left, :right
1208
+ def initialize(list, block)
1209
+ @list, @block, @left, @right = list, block, [], EmptyList
1210
+ end
1211
+
1212
+ def next_item
1213
+ unless @list.empty?
1214
+ item = @list.head
1215
+ if @block.call(item)
1216
+ @left << item
1217
+ @list = @list.tail
1218
+ else
1219
+ @right = @list
1220
+ @list = EmptyList
1221
+ end
1222
+ end
1223
+ end
1224
+
1225
+ def done?
1226
+ @list.empty?
1227
+ end
1228
+
1229
+ class Left < Realizable
1230
+ def initialize(splitter, buffer, mutex)
1231
+ super()
1232
+ @splitter, @buffer, @mutex = splitter, buffer, mutex
1233
+ end
1234
+
1235
+ def realize
1236
+ @mutex.synchronize do
1237
+ return if @head != Undefined # another thread got ahead of us
1238
+ while true
1239
+ if !@buffer.empty?
1240
+ @head = @buffer.shift
1241
+ @tail = Left.new(@splitter, @buffer, @mutex)
1242
+ @splitter, @buffer, @mutex = nil, nil, nil
1243
+ return
1244
+ elsif @splitter.done?
1245
+ @head, @size, @tail = nil, 0, self
1246
+ @splitter, @buffer, @mutex = nil, nil, nil
1247
+ return
1248
+ else
1249
+ @splitter.next_item
1250
+ end
1251
+ end
1252
+ end
1253
+ end
1254
+ end
1255
+
1256
+ class Right < Realizable
1257
+ def initialize(splitter, mutex)
1258
+ super()
1259
+ @splitter, @mutex = splitter, mutex
1260
+ end
1261
+
1262
+ def realize
1263
+ @mutex.synchronize do
1264
+ return if @head != Undefined
1265
+ @splitter.next_item until @splitter.done?
1266
+ if @splitter.right.empty?
1267
+ @head, @size, @tail = nil, 0, self
1268
+ else
1269
+ @head, @tail = @splitter.right.head, @splitter.right.tail
1270
+ end
1271
+ @splitter, @mutex = nil, nil
1272
+ end
1273
+ end
635
1274
  end
636
1275
  end
637
1276
 
638
- # A list without any elements
1277
+ # A list without any elements. This is a singleton, since all empty lists are equivalent.
639
1278
  #
640
- # This is a singleton, since all empty lists are equivalent. It is used
641
- # as a terminating element in a chain of +Sequence+ instances.
642
1279
  module EmptyList
643
1280
  class << self
644
1281
  include List
645
1282
 
1283
+ # There is no first item in an empty list, so return `nil`.
1284
+ # @return [nil]
646
1285
  def head
647
1286
  nil
648
1287
  end
649
1288
 
1289
+ # There are no subsequent elements, so return an empty list.
1290
+ # @return [self]
650
1291
  def tail
651
1292
  self
652
1293
  end
@@ -655,6 +1296,8 @@ module Hamster
655
1296
  true
656
1297
  end
657
1298
 
1299
+ # Return the number of items in this `List`.
1300
+ # @return [Integer]
658
1301
  def size
659
1302
  0
660
1303
  end