diff --git a/changelog.md b/changelog.md index d62e96bbed78b..755f1b78cb33f 100644 --- a/changelog.md +++ b/changelog.md @@ -52,6 +52,8 @@ - `writeStackTrace` is available in JS backend now. +- Added `algorithm.merge`. + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 2430e7330c2c6..17a0e8d5afa95 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -46,6 +46,8 @@ ## * `sequtils module`_ for working with the built-in seq type ## * `tables module`_ for sorting tables +import std/private/since + type SortOrder* = enum Descending, Ascending @@ -556,6 +558,66 @@ proc isSorted*[T](a: openArray[T], order = SortOrder.Ascending): bool = assert isSorted(e) == false isSorted(a, system.cmp[T], order) +proc merge*[T](x, y: openArray[T], cmp: proc(x, y: T): int {.closure.} = nil): seq[T] {.since: (1, 5, 1).} = + ## Merges two sorted `openArray`. `x` and `y` are assumed to be sorted. + ## If you do not wish to provide your own `cmp`, + ## you may use `system.cmp` or instead call the overloaded + ## version of `merge`, which uses `system.cmp`. + ## + ## **See also:** + ## * `merge proc<#merge,openArray[T],openArray[T]>`_ + runnableExamples: + let x = @[1, 3, 6] + let y = @[2, 3, 4] + + let res = x.merge(y, system.cmp[int]) + assert res.isSorted + assert res == @[1, 2, 3, 3, 4, 6] + + let + sizeX = x.len + sizeY = y.len + + result = newSeq[T](sizeX + sizeY) + var cmp = cmp + if cmp == nil: + when compiles(system.cmp[T]): + cmp = system.cmp[T] + else: + doAssert false + + var + ix = 0 + iy = 0 + i = 0 + + while true: + if ix == sizeX: + while iy < sizeY: + result[i] = y[iy] + inc i + inc iy + return + + if iy == sizeY: + while ix < sizeX: + result[i] = x[ix] + inc i + inc ix + return + + let itemX = x[ix] + let itemY = y[iy] + + if cmp(itemX, itemY) > 0: + result[i] = itemY + inc iy + else: + result[i] = itemX + inc ix + + inc i + proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = ## Produces the Cartesian product of the array. Warning: complexity ## may explode. diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim index 9dec68f032ec3..c5419d9fb3525 100644 --- a/tests/stdlib/talgorithm.nim +++ b/tests/stdlib/talgorithm.nim @@ -113,3 +113,82 @@ block: doAssert binarySearch(moreData, 6) == -1 doAssert binarySearch(moreData, 4711) == 4 doAssert binarySearch(moreData, 4712) == -1 + +# merge +block: + var x = @[1, 7, 8, 11, 21, 33, 45, 99] + var y = @[6, 7, 9, 12, 57, 66] + + let merged = merge(x, y) + doAssert merged.isSorted + doAssert merged == @[1, 6, 7, 7, 8, 9, 11, 12, 21, 33, 45, 57, 66, 99] + +block: + var x = @[111, 88, 76, 56, 45, 31, 22, 19, 11, 3] + var y = @[99, 85, 83, 82, 69, 64, 48, 42, 33, 31, 26, 13] + + let merged = merge(x, y, SortOrder.Descending) + doAssert merged.isSorted(SortOrder.Descending) + doAssert merged == @[111, 99, 88, 85, 83, 82, 76, 69, 64, 56, 48, 45, 42, 33, 31, 31, 26, 22, 19, 13, 11, 3] + # doAssert merged == @[99, 85, 83, 82, 69, 64, 48, 42, 33, 31, 26, 13, 111, 88, 76, 56, 45, 31, 22, 19, 11, 3] + +block: + var x: seq[int] = @[] + var y = @[1] + + let merged = merge(x, y) + doAssert merged.isSorted + doAssert merged.isSorted(SortOrder.Descending) + doAssert merged == @[1] + +block: + var x = [1, 3, 5, 5, 7] + var y: seq[int] = @[] + + let merged = merge(x, y) + doAssert merged.isSorted + doAssert merged == @x + +block: + var x: array[0, int] + var y = [1, 4, 6, 7, 9] + + let merged = merge(x, y) + doAssert merged.isSorted + doAssert merged == @y + +block: + var x: array[0, int] + var y: array[0, int] + + let merged = merge(x, y) + doAssert merged.isSorted + doAssert merged.len == 0 + +block: + var x: seq[int] + var y: seq[int] + + let merged = merge(x, y) + doAssert merged.isSorted + doAssert merged.len == 0 + +block: + type + Record = object + id: int + + proc r(id: int): Record = + Record(id: id) + + proc cmp(x, y: Record): int = + if x.id == y.id: return 0 + if x.id < y.id: return -1 + result = 1 + + var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)] + var y = @[r(4), r(7), r(12), r(13), r(77), r(99)] + + let merged = merge(x, y, cmp) + doAssert merged.isSorted(cmp) + doAssert merged.len == 12