@effekt-lang/effekt 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/effekt CHANGED
Binary file
@@ -3,6 +3,7 @@ module array
3
3
  import effekt
4
4
  import exception
5
5
  import list
6
+ import option
6
7
 
7
8
  /// A mutable 0-indexed fixed-sized array.
8
9
  extern type Array[T]
@@ -18,12 +19,17 @@ extern global def allocate[T](size: Int): Array[T] =
18
19
  """
19
20
  vm "array::allocate(Int)"
20
21
 
22
+ /// Fills a given array in-place.
23
+ def fill[T](arr: Array[T], filler: T): Unit = {
24
+ arr.foreachIndex { (idx, _) =>
25
+ arr.unsafeSet(idx, filler)
26
+ }
27
+ }
28
+
21
29
  /// Creates a new Array of size `size` filled with the value `init`
22
30
  def array[T](size: Int, init: T): Array[T] = {
23
31
  val arr = allocate[T](size);
24
- each(0, size) { i =>
25
- unsafeSet(arr, i, init)
26
- };
32
+ arr.fill(init)
27
33
  arr
28
34
  }
29
35
 
@@ -81,8 +87,6 @@ extern global def unsafeSet[T](arr: Array[T], index: Int, value: T): Unit =
81
87
  """
82
88
  vm "array::unsafeSet[T](Array[T], Int, T)"
83
89
 
84
-
85
-
86
90
  // Derived operations:
87
91
 
88
92
  /// Gets the element of the `arr` at given `index` in constant time,
@@ -107,6 +111,9 @@ def build[T](size: Int) { index: Int => T }: Array[T] = {
107
111
  arr
108
112
  }
109
113
 
114
+ /// Resizes an array to a given size.
115
+ ///
116
+ /// O(N + M)
110
117
  def resize[T](source: Array[T], size: Int): Array[T] = {
111
118
  val target = allocate(size)
112
119
  val n = min(source.size, target.size)
@@ -147,6 +154,27 @@ def copy[T](from: Array[T], start: Int, to: Array[T], offset: Int, length: Int):
147
154
 
148
155
  // Utility functions:
149
156
 
157
+ /// Swap the value at index i with that at index j.
158
+ /// Warning: undefined behaviour if the indices are out bound.
159
+ ///
160
+ /// O(1)
161
+ def unsafeSwap[A](arr: Array[A], i: Int, j: Int): Unit = {
162
+ val tmp = arr.unsafeGet(i)
163
+ arr.unsafeSet(i, arr.unsafeGet(j))
164
+ arr.unsafeSet(j, tmp)
165
+ }
166
+
167
+ /// Swap the value at index i with that at index j.
168
+ ///
169
+ /// O(1)
170
+ def swap[A](arr: Array[A], i: Int, j: Int): Unit / Exception[OutOfBounds] = {
171
+ if (i < 0 || i >= arr.size || j < 0 || j >= arr.size) do raise(OutOfBounds(), "Index out of bounds")
172
+ arr.unsafeSwap(i, j)
173
+ }
174
+
175
+ /// Converts the given array into a list.
176
+ ///
177
+ /// O(N)
150
178
  def toList[T](arr: Array[T]): List[T] = {
151
179
  var i = arr.size - 1;
152
180
  var l = Nil[T]()
@@ -157,34 +185,380 @@ def toList[T](arr: Array[T]): List[T] = {
157
185
  l
158
186
  }
159
187
 
188
+ /// Traverse an array, applying the given action on every element.
189
+ ///
190
+ /// O(N)
160
191
  def foreach[T](arr: Array[T]){ action: T => Unit }: Unit =
161
192
  each(0, arr.size) { i =>
162
193
  action(arr.unsafeGet(i))
163
194
  }
164
195
 
196
+ /// Traverse an array, applying the given action on every element.
197
+ ///
198
+ /// O(N)
165
199
  def foreach[T](arr: Array[T]){ action: (T) {Control} => Unit }: Unit =
166
200
  each(0, arr.size) { (i) {label} =>
167
201
  action(arr.unsafeGet(i)) {label}
168
202
  }
169
203
 
204
+ /// Traverse an array, applying the given action on every element and its (zero-based) index.
205
+ ///
206
+ /// O(N)
170
207
  def foreachIndex[T](arr: Array[T]){ action: (Int, T) => Unit }: Unit =
171
208
  each(0, arr.size) { i =>
172
209
  action(i, arr.unsafeGet(i))
173
210
  }
174
211
 
212
+ /// Traverse an array, applying the given action on every element and its (zero-based) index.
213
+ ///
214
+ /// O(N)
175
215
  def foreachIndex[T](arr: Array[T]){ action: (Int, T) {Control} => Unit }: Unit =
176
216
  each(0, arr.size) { (i) {label} =>
177
217
  action(i, arr.unsafeGet(i)) {label}
178
218
  }
179
219
 
180
- def sum(list: Array[Int]): Int = {
220
+ /// Sum the elements of the array.
221
+ ///
222
+ /// O(N)
223
+ def sum(arr: Array[Int]): Int = {
181
224
  var acc = 0
182
- list.foreach { x =>
225
+ arr.foreach { x =>
183
226
  acc = acc + x
184
227
  }
185
228
  acc
186
229
  }
187
230
 
231
+ /// Checks whether two given arrays are equal with respect to an equalty funciton defined on their element.
232
+ ///
233
+ /// O(N)
234
+ def equals[A](this: Array[A], other: Array[A]) { eq: (A, A) => Bool }: Bool = {
235
+ if (this.size != other.size) false
236
+ else {
237
+ var res = true
238
+ this.foreachIndex { (idx, elem) {label} =>
239
+ if (not(eq(elem, other.unsafeGet(idx)))) {
240
+ res = false
241
+ label.break()
242
+ }
243
+ }
244
+ res
245
+ }
246
+ }
247
+
248
+ /// Map a function `f` over the elements in a given array.
249
+ /// Modifies the array in-place.
250
+ ///
251
+ /// O(N)
252
+ def map[A](arr: Array[A]) { f: A => A }: Unit = {
253
+ arr.foreachIndex { (idx, elem) =>
254
+ arr.unsafeSet(idx, f(arr.unsafeGet(idx)))
255
+ }
256
+ }
257
+
258
+ /// Map a function over elements.
259
+ /// Allocates a new array of the same size.
260
+ ///
261
+ /// O(N)
262
+ def mapped[A, B](arr: Array[A]) { f: A => B }: Array[B] = {
263
+ val res = allocate(arr.size)
264
+ arr.foreachIndex { (idx, elem) =>
265
+ res.unsafeSet(idx, f(arr.unsafeGet(idx)))
266
+ }
267
+ res
268
+ }
269
+
270
+ /// Reverses an array in-place.
271
+ ///
272
+ /// O(N)
273
+ def reverse[A](arr: Array[A]): Unit = {
274
+ val size = arr.size()
275
+ val mid = size / 2
276
+
277
+ // Swap elements from start and end, moving towards the middle
278
+ each(0, mid) { i =>
279
+ val temp = arr.unsafeGet(i)
280
+ val j = size - 1 - i
281
+ val other = arr.unsafeGet(j)
282
+ arr.unsafeSet(i, other)
283
+ arr.unsafeSet(j, temp)
284
+ }
285
+ }
286
+ /// Creates a new array with elements in reverse order.
287
+ ///
288
+ /// O(N)
289
+ def reversed[A](arr: Array[A]): Array[A] = {
290
+ val size = arr.size()
291
+ val result = allocate[A](size)
292
+
293
+ // Copy elements in reverse order to new array
294
+ each(0, size) { i =>
295
+ val value = arr.unsafeGet(i)
296
+ result.unsafeSet(size - 1 - i, value)
297
+ }
298
+ result
299
+ }
300
+
301
+ /// Fold an array using `f`, starting from the left given a starting value.
302
+ ///
303
+ /// O(N)
304
+ def foldLeft[A, B](arr: Array[A], init: B) { f: (B, A) => B }: B = {
305
+ var acc = init;
306
+ arr.foreach { x => acc = f(acc, x) };
307
+ acc
308
+ }
309
+
310
+ /// Fold an array using `f`, starting from the right given a starting value.
311
+ ///
312
+ /// O(N)
313
+ def foldRight[A, B](arr: Array[A], init: B) { f: (A, B) => B }: B = {
314
+ var acc = init;
315
+ arr.reversed.foreach { x => acc = f(x, acc) };
316
+ acc
317
+ }
318
+
319
+ /// Filters a given array with respect to a given predicate such that a
320
+ /// new array containing all the elements for which the predicate
321
+ /// holds is allocated.
322
+ ///
323
+ /// O(N)
324
+ def filtered[A](arr: Array[A]) { shouldKeep: A => Bool }: Array[A] = {
325
+ val res = arr.copy()
326
+ var idx = 0
327
+ arr.foreach { x =>
328
+ if (shouldKeep(x)) {
329
+ res.unsafeSet(idx, x)
330
+ idx = idx + 1
331
+ }
332
+ }
333
+ res.resize(idx)
334
+ }
335
+
336
+ /// Check if predicate is true for all elements.
337
+ ///
338
+ /// O(N)
339
+ def all[A](arr: Array[A]) { predicate: A => Bool }: Bool = {
340
+ var result = true
341
+ arr.foreach { (x) {label} =>
342
+ if (not(predicate(x))) {
343
+ result = false
344
+ label.break()
345
+ }
346
+ }
347
+ result
348
+ }
349
+
350
+ /// Check if predicate is true for at least one element.
351
+ ///
352
+ /// O(N)
353
+ def any[A](arr: Array[A]) { predicate: A => Bool }: Bool = {
354
+ not(arr.all { x => not(predicate(x)) })
355
+ }
356
+
357
+ /// Count elements satisfying the predicate.
358
+ ///
359
+ /// O(N)
360
+ def count[A](arr: Array[A]) { predicate: A => Bool }: Int = {
361
+ var count = 0
362
+ arr.foreach { x =>
363
+ if (predicate(x)) count = count + 1
364
+ }
365
+ count
366
+ }
367
+
368
+ /// Take first n elements.
369
+ /// Returns a new array.
370
+ ///
371
+ /// O(N)
372
+ def take[A](arr: Array[A], n: Int): Array[A] = {
373
+ val len = min(max(0, n), arr.size)
374
+ val result = allocate[A](len)
375
+ each(0, len) { i =>
376
+ result.unsafeSet(i, arr.unsafeGet(i))
377
+ }
378
+ result
379
+ }
380
+
381
+ // Drop first n elements.
382
+ /// Returns a new array.
383
+ ///
384
+ /// O(N)
385
+ def drop[A](arr: Array[A], n: Int): Array[A] = {
386
+ val start = min(max(0, n), arr.size)
387
+ val len = arr.size - start
388
+ val result = allocate[A](len)
389
+ each(0, len) { i =>
390
+ result.unsafeSet(i, arr.unsafeGet(i + start))
391
+ }
392
+ result
393
+ }
394
+
395
+ /// Return a slice from start (inclusive) to end (exclusive).
396
+ /// Returns a new array.
397
+ ///
398
+ /// O(N)
399
+ def sliced[A](arr: Array[A], start: Int, end: Int): Array[A] = {
400
+ val validStart = min(max(0, start), arr.size)
401
+ val validEnd = min(max(validStart, end), arr.size)
402
+ val len = validEnd - validStart
403
+ val result = allocate[A](len)
404
+ each(0, len) { i =>
405
+ result.unsafeSet(i, arr.unsafeGet(i + validStart))
406
+ }
407
+ result
408
+ }
409
+
410
+ /// Zip two arrays into an array of pairs.
411
+ /// Length is minimum of input lengths.
412
+ ///
413
+ /// O(min(N, M))
414
+ def zipped[A, B](this: Array[A], other: Array[B]): Array[(A, B)] = {
415
+ val len = min(this.size, other.size)
416
+ val result = allocate[(A, B)](len)
417
+ each(0, len) { i =>
418
+ result.unsafeSet(i, (this.unsafeGet(i), other.unsafeGet(i)))
419
+ }
420
+ result
421
+ }
422
+
423
+ /// Combine two arrays using given function.
424
+ /// Length is minimum of input lengths.
425
+ ///
426
+ /// O(min(N, M))
427
+ def zippedWith[A, B, C](this: Array[A], other: Array[B]) { f: (A, B) => C }: Array[C] = {
428
+ val len = min(this.size, other.size)
429
+ val result = allocate[C](len)
430
+ each(0, len) { i =>
431
+ result.unsafeSet(i, f(this.unsafeGet(i), other.unsafeGet(i)))
432
+ }
433
+ result
434
+ }
435
+
436
+ /// Produce a pair of arrays from an array of pairs.
437
+ /// Allocates two new arrays as a result.
438
+ ///
439
+ /// O(N)
440
+ def unzipped[A, B](arr: Array[(A, B)]): (Array[A], Array[B]) = {
441
+ val arr1 = allocate(arr.size)
442
+ val arr2 = allocate(arr.size)
443
+ arr.foreachIndex { (idx, tpl) =>
444
+ val (x, y) = tpl
445
+ arr1.unsafeSet(idx, x)
446
+ arr2.unsafeSet(idx, y)
447
+ }
448
+ (arr1, arr2)
449
+ }
450
+
451
+ /// Partition a given array into two arrays.
452
+ /// The left array contains the elements that satsify the predicate,
453
+ /// the right array contains the elements that do not.
454
+ ///
455
+ /// O(N)
456
+ def partioned[A](arr: Array[A]) { pred: A => Bool }: (Array[A], Array[A]) = {
457
+ val tcases = arr.copy()
458
+ val fcases = arr.copy()
459
+ var tidx = 0
460
+ var fidx = 0
461
+ arr.foreach { x =>
462
+ if (pred(x)) {
463
+ tcases.unsafeSet(tidx, x)
464
+ tidx = tidx + 1
465
+ } else {
466
+ fcases.unsafeSet(fidx, x)
467
+ fidx = fidx + 1
468
+ }
469
+ }
470
+ (tcases.resize(tidx), fcases.resize(fidx))
471
+ }
472
+
473
+ /// Get the _first_ index of the last occurance of `elem`, if there is any.
474
+ ///
475
+ /// O(N)
476
+ def indexOf[A](arr: Array[A], toFind: A) { eq: (A, A) => Bool }: Option[Int] = {
477
+ var found = None[Int]()
478
+ arr.foreachIndex { (idx, elem) {label} =>
479
+ if (eq(elem, toFind) && found.isEmpty) {
480
+ found = Some(idx)
481
+ label.break()
482
+ }
483
+ }
484
+ found
485
+ }
486
+
487
+ /// Get the _last_ index of the last occurance of `elem`, if there is any.
488
+ ///
489
+ /// O(N)
490
+ def lastIndexOf[A](arr: Array[A], toFind: A) { eq: (A, A) => Bool }: Option[Int] = {
491
+ var lastFound = None[Int]()
492
+ arr.foreachIndex { (idx, elem) =>
493
+ if (eq(elem, toFind)) {
494
+ lastFound = Some(idx)
495
+ }
496
+ }
497
+ lastFound
498
+ }
499
+
500
+ /// Perform binary search to find the index of a given element.
501
+ ///
502
+ /// O(log N)
503
+ def binarySearch[A](arr: Array[A], toFind: A) { ord: (A, A) => Ordering }: Option[Int] = {
504
+ def binarySearch(lowerIdx: Int, upperIdx: Int): Option[Int] = {
505
+ if (lowerIdx > upperIdx) None()
506
+ else {
507
+ val mid = lowerIdx + (upperIdx - lowerIdx) / 2
508
+ val current = arr.unsafeGet(mid)
509
+ ord(current, toFind) match {
510
+ case Less() => binarySearch(mid + 1, upperIdx)
511
+ case Greater() => binarySearch(lowerIdx, mid - 1)
512
+ case Equal() => Some(mid)
513
+ }
514
+ }
515
+ }
516
+ binarySearch(0, arr.size - 1)
517
+ }
518
+
519
+ /// Sort an array in-place using provided comparison function.
520
+ /// Note: sort is unstable since it uses QuickSort (unlike list::sortBy)
521
+ ///
522
+ /// O(N log N) average case
523
+ def sort[A](arr: Array[A]) { ord: (A, A) => Ordering }: Unit = {
524
+ def partition(low: Int, high: Int): Int = {
525
+ val pivot = arr.unsafeGet(high)
526
+ var i = low - 1
527
+
528
+ each(low, high) { j =>
529
+ ord(arr.unsafeGet(j), pivot) match {
530
+ case Less() =>
531
+ i = i + 1
532
+ unsafeSwap(arr, i, j)
533
+ case _ => ()
534
+ }
535
+ }
536
+ unsafeSwap(arr, i + 1, high)
537
+ i + 1
538
+ }
539
+
540
+ def quickSort(low: Int, high: Int): Unit = {
541
+ if (low < high) {
542
+ val par = partition(low, high)
543
+ quickSort(low, par - 1)
544
+ quickSort(par + 1, high)
545
+ }
546
+ }
547
+
548
+ quickSort(0, arr.size - 1)
549
+ }
550
+
551
+ /// Sort an array using provided comparison function.
552
+ /// A newly allocated sorted array is returned as result, i.e., the sorting is not in-place.
553
+ /// Note: sort is unstable since it uses QuickSort (unlike list::sortBy)
554
+ ///
555
+ /// O(N log N) average case
556
+ def sorted[A](arr: Array[A]) { ord: (A, A) => Ordering }: Array[A] = {
557
+ val res = arr.copy()
558
+ res.sort { ord }
559
+ res
560
+ }
561
+
188
562
  // Show Instances
189
563
  // --------------
190
564
 
@@ -30,6 +30,17 @@ effect stop(): Nothing
30
30
  def for[A] { stream: () => Unit / emit[A] } { action: A => Unit }: Unit =
31
31
  returning::for[A, Unit]{stream}{action}
32
32
 
33
+ /// Runs the stream as a message queue,
34
+ /// handling each of its elements that can produce new elements ("messages"),
35
+ /// until a fixed point is reached.
36
+ def fix[T] { one: T => Unit / emit[T] } { stream: => Unit / emit[T] }: Unit =
37
+ try {
38
+ stream()
39
+ } with emit[T] { value =>
40
+ fix[T] {one} { one(value) }
41
+ resume(())
42
+ }
43
+
33
44
  /// Turns a `list` into a producer of a push stream
34
45
  /// by emitting each contained value left-to-right.
35
46
  def each[A](list: List[A]): Unit / emit[A] =
@@ -0,0 +1 @@
1
+