Skip to content

Instantly share code, notes, and snippets.

@AdrianV
Created April 16, 2013 16:39
Show Gist options
  • Save AdrianV/5397444 to your computer and use it in GitHub Desktop.
Save AdrianV/5397444 to your computer and use it in GitHub Desktop.
ranges.nim is a proof of concept how to implement abstract ranges in Nimrod
# ranges.nim
type
TDownSlice* {.final, pure.}[T] = object
a*, b*: T
TCountUpRange* [T] = ref RCountUpRange[T]
RCountUpRange [T] = object
cur: T
a: T
last: T
TCountDownRange* [T] = ref RCountDownRange[T]
RCountDownRange [T] = object
cur: T
a: T
last: T
TSeqRange* [T] = ref RSeqRange[T]
RSeqRange {.pure, shallow.} [T] = object
data: seq[T]
cur: int
last: int
template forEach* (el, ran: expr; loop: stmt): stmt {.immediate.} =
var lran = toRange(ran)
while not empty(lran):
block:
let `el` {.inject.} = front(lran)
loop
popFront(lran)
template reduce* (ran, fun: expr): expr =
#mixin empty, front, popFront
var lran = toRange(ran)
var res: type(front(lran))
if not empty(lran) :
res = front(lran)
popFront(lran)
while not empty(lran):
let x = front(lran)
popFront(lran)
let
a {.inject.} = res
b {.inject.} = x
res = fun
res
proc find[R1, T, R2] (ran: R1, value: T): R2 =
mixin empty, front, popFront, toRange
result = toRange(ran)
when compiles(fastFind(result, value)):
result = fastFind(result, value)
else :
while not empty(result):
if front(result) == value : break
popFront(result)
template toRange* [T](ran: TCountUpRange[T]): TCountUpRange[T] = ran
proc toRange* [T](slic: TSlice[T]): TCountUpRange[T] {.inline.} =
new(result)
result.cur = slic.a
result.a = T(slic.a)
result.last = slic.b
template empty* [T](ran: TCountUpRange[T]): bool =
ran.cur > ran.last
template front* [T](ran: TCountUpRange[T]): T =
ran.cur
template popFront* [T](ran: TCountUpRange[T]) =
ran.cur += 1
proc fastFind* [T] (ran: TCountUpRange[T], value: T): TCountUpRange[T] {.inline.} =
if value >= ran.cur and value <= ran.last :
ran.cur = value
else :
ran.cur = ran.last
popFront(ran)
return ran
proc `-->`* [T](a, b: T): TDownSlice[T] {.noSideEffect, inline.} =
result.a = a
result.b = b
template toRange* [T](ran: TCountDownRange[T]): TCountDownRange[T] = ran
proc toRange* [T] (val: TDownSlice[T]): TCountDownRange[T] =
new(result)
result.cur = val.a
result.a = val.a
result.last = val.b
template empty* [T](ran: TCountDownRange[T]): bool =
ran.cur < ran.last
template front* [T](ran: TCountDownRange[T]): T =
ran.cur
template popFront* [T] (ran: TCountDownRange[T]) =
ran.cur -= 1
proc fastFind* [T] (ran: TCountDownRange[T], value: T): TCountDownRange[T] {.inline.} =
if value >= ran.cur and value <= ran.last :
ran.cur = value
else :
ran.cur = ran.last - T(1)
return ran
template toRange* [T](ran: TSeqRange[T]): TSeqRange[T] = ran
proc toRange* [T] (arr: seq[T]): TSeqRange[T] =
new(result)
result.data = arr
result.cur = 0
result.last = arr.len
proc slice* [T] (arr: seq[T], slic: TSlice[int]): TSeqRange[T] =
new(result)
result.data = arr
result.cur = max(0, slic.a)
result.last = min(arr.len, slic.b+1)
template empty* [T] (ran: TSeqRange[T]): bool =
ran.cur >= ran.last
template front* [T] (ran: TSeqRange[T]): T =
ran.data[ran.cur]
template popFront* [T] (ran: TSeqRange[T]) =
inc(ran.cur)
when isMainModule:
var
data = @[1,2,3,4,5,6,7,8]
forEach x, data:
echo x
forEach x, 2..8:
echo x
forEach x, 2.1 .. 8.2:
echo x
forEach x,data.slice(2..4):
echo x
block:
var r = data.slice(2..4)
forEach x, r:
echo x
forEach x, 8.2 --> 2.1 :
echo x
echo reduce(1..10, a + b)
echo reduce(data, a + b)
echo reduce(@[2, 7, 8, -1], (if a < b : a else: b))
#setLen(data,0)
echo reduce(data, (if a < b : a else: b))
forEach x, find(data, 4):
echo x
echo reduce(1..1000_000_000, a+b)
echo reduce(1..10, a * b)
iterator elements [R,T] (ran: R): T {.inline.} =
mixin empty, front, popFront, toRange
var lran = toRange(ran)
while not empty(lran):
yield front(lran)
popFront(lran)
for x in elements[TSeqRange[int],int](data.slice(1..3)):
echo x
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment