Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add algorithm.merge #16182

Merged
merged 18 commits into from
Mar 23, 2021
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@

- `writeStackTrace` is available in JS backend now.

- Added `algorithm.merge`.

## Language changes

- `nimscript` now handles `except Exception as e`.
Expand Down
72 changes: 72 additions & 0 deletions lib/pure/algorithm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
## * `sequtils module<sequtils.html>`_ for working with the built-in seq type
## * `tables module<tables.html>`_ for sorting tables

import std/private/since
ringabout marked this conversation as resolved.
Show resolved Hide resolved

type
SortOrder* = enum
Descending, Ascending
Expand Down Expand Up @@ -556,6 +558,76 @@ proc isSorted*[T](a: openArray[T], order = SortOrder.Ascending): bool =
assert isSorted(e) == false
isSorted(a, system.cmp[T], order)

proc merge*[T](
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
x, y: openArray[T], cmp: proc(x, y: T
): int {.closure.}, order = SortOrder.Ascending): seq[T] {.since: (1, 5, 1).} =
## Merges two sorted `openArray`. `x` and `y` are assumed to be sorted.
ringabout marked this conversation as resolved.
Show resolved Hide resolved
ringabout marked this conversation as resolved.
Show resolved Hide resolved
## 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
ix = 0
iy = 0
i = 0

ringabout marked this conversation as resolved.
Show resolved Hide resolved
while true:
if ix == sizeX:
while iy < sizeY:
ringabout marked this conversation as resolved.
Show resolved Hide resolved
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) * order > 0:
result[i] = itemY
inc iy
else:
result[i] = itemX
inc ix

inc i

proc merge*[T](x, y: openArray[T], order = SortOrder.Ascending): seq[T] {.since: (1, 5, 1).} =
ringabout marked this conversation as resolved.
Show resolved Hide resolved
## Shortcut version of `merge` that uses `system.cmp[T]` as the comparison function.
##
## **See also:**
## * `merge proc<#merge,openArray[T],openArray[T],proc(T,T)>`_
runnableExamples:
let x = [5,10,15,20,25]
let y = [50,40,30,20,10].sorted
ringabout marked this conversation as resolved.
Show resolved Hide resolved

let res = x.merge(y)
assert res.isSorted
assert res == @[5, 10, 10, 15, 20, 20, 25, 30, 40, 50]
merge(x, y, system.cmp, order)

proc product*[T](x: openArray[seq[T]]): seq[seq[T]] =
## Produces the Cartesian product of the array. Warning: complexity
## may explode.
Expand Down
79 changes: 79 additions & 0 deletions tests/stdlib/talgorithm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,82 @@ block:
doAssert binarySearch(moreData, 6) == -1
doAssert binarySearch(moreData, 4711) == 4
doAssert binarySearch(moreData, 4712) == -1

# merge
ringabout marked this conversation as resolved.
Show resolved Hide resolved
block:
ringabout marked this conversation as resolved.
Show resolved Hide resolved
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
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged == @[1, 6, 7, 7, 8, 9, 11, 12, 21, 33, 45, 57, 66, 99]
ringabout marked this conversation as resolved.
Show resolved Hide resolved

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)
ringabout marked this conversation as resolved.
Show resolved Hide resolved
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)
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged == @[1]

block:
var x = [1, 3, 5, 5, 7]
var y: seq[int] = @[]

let merged = merge(x, y)
doAssert merged.issorted
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged == @x

block:
var x: array[0, int]
var y = [1, 4, 6, 7, 9]

let merged = merge(x, y)
doAssert merged.issorted
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged == @y

block:
var x: array[0, int]
var y: array[0, int]

let merged = merge(x, y)
doAssert merged.issorted
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged.len == 0

block:
var x: seq[int]
var y: seq[int]

let merged = merge(x, y)
doAssert merged.issorted
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged.len == 0

block:
type
Record = object
id: int

proc r(id: int): Record =
Record(id: id)

proc cmp(x, y: Record): int =
ringabout marked this conversation as resolved.
Show resolved Hide resolved
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)
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged.len == 12