3

I would like to use nimbioseq and iterate two files with the same number of sequences (using the readSeq()iterator), as:

for seq1, seq2 in readSeq(file1), readSeq(file2):
  echo seq1.id, "\t", seq2.id

For this scenario I suppose I need some sort of "zipping" operator, which I couldn't understand how to use [ found this: https://hookrace.net/nim-iterutils/iterutils.html#zip.i,,Iterable[S] ].

or alternatively understand how to get a single "iteration" outside a for loop (if possible):

for seq1 in readSeq(file1):
  let seq2 = readSeq(file2);
  echo seq1.id, "\t", seq2.id

Thanks for you help!

pietroppeter
  • 1,433
  • 13
  • 30
Andrea T.
  • 920
  • 4
  • 15
  • 1
    I really want to thank you all for the fantastic answers. I did some tests but need to finish and will provide feedback asap. – Andrea T. Oct 01 '20 at 08:06

3 Answers3

2

toClosure from iterutils is limited, but you can:

import iterutils

template initClosure(id,iter:untyped) =
  let id = iterator():auto{.closure.} =
    for x in iter:
      yield x

initClosure(f1,readSeq(file1))
#creates a new closure iterator, 'f1'

initClosure(f2,readSeq(file2))
#creates a new closure iterator, 'f2'

for seq1,seq2 in zip(f1,f2):
  echo seq1.id,"\t",seq2.id

Edit: thanks to @pietropeter for pointing out the bug, here's their example rewritten using this template:

import iterutils
template initClosure(id:untyped,iter:untyped) =
  let id = iterator():auto {.closure.} =
    for x in iter:
      yield x

iterator letters: auto =
  for c in 'a' .. 'z':
    yield c

# Now requires a parameter
iterator numbers(s: int): int =
  var n = s
  while true:
    yield n
    inc n

initClosure(cletter,letters())
initClosure(numbers8,numbers(8))

for (c, n) in zip(cletter, numbers8):
  echo c, n
Andrea T.
  • 920
  • 4
  • 15
shirleyquirk
  • 1,527
  • 5
  • 21
  • I like the template approach and I was trying to apply it to the extended version but it fails: https://play.nim-lang.org/#ix=2z4b what am I doing wrong? – pietroppeter Sep 28 '20 at 18:53
  • 1
    thanks for pointing that out, i had id and iter both quoted still from my macro solution so my bad. the only change to your code is adding parens after letters. – shirleyquirk Sep 29 '20 at 01:48
  • Perfect! For me now this is the best answer – pietroppeter Sep 29 '20 at 06:10
1

I'm going to use this iterators code from Manual, and insert your problem in it. I'm sure it has room for improvement:

type
  Task = iterator (r: var int)

iterator f1(r: var int){.closure.} =
  for n in [1, 3, 5]:
    r = n
    yield

iterator f2(r: var int){.closure.} =
  for n in [2, 4, 6]:
    r = n
    yield

proc runTasks(t: varargs[Task]) =
  var ticker = 0
  var r: int

  while true:
    var x = t[ticker mod t.len]
    x(r)
    echo r
    if finished(x): break
    inc ticker

runTasks(f1, f2)

You'll see in the output 1,2,3,4,5,6,6 (finished is prone to error, as stated in the manual, and returns the last item twice). You have to update the code, replacing r: var int with whatever type returns readSeq(file) (r: var Record, I think), and replace the iterators for n in [1, 2, 3] with for s in readSeq(file).

xbello
  • 7,223
  • 3
  • 28
  • 41
1

If the type of behaviour you want is that of zip, the one from iterutils seems to work fine. The only caveat is that it requires closure iterators (see manual for the difference between inline and closure iterators). Example (https://play.nim-lang.org/#ix=2yXV):

import iterutils

iterator letters: char {.closure.} =
  for c in 'a' .. 'z':
    yield c

iterator numbers: int {.closure.}=
  var n = 1
  while true:
    yield n
    inc n

for (c, n) in zip(letters, numbers):
  echo c, n

I see that readseq in nimbioseq is not closure but probably something like this could work (edit: its should not, see below):

iterator closureReadSeqs(filename: string): Record {.closure.} =
  for rec in readSeqs(filename):
    yield rec

Edit

For the case of iterator with a parameter in the comments, the fix is to have a proc that returns an iterator (which will be a closure iterator by default in this case). Updated example (https://play.nim-lang.org/#ix=2z0e):

import iterutils

iterator letters: char {.closure.} =
  for c in 'a' .. 'z':
    yield c

# Now requires a parameter
proc numbers(s: int): iterator(): int =
  return iterator(): int =
    var n = s
    while true:
      yield n
      inc n

let numbers8 = numbers(8)
for (c, n) in zip(letters, numbers8):
  echo c, n

Now my best guess on how to make this work for nimbioseq is:

proc closureReadSeqs(filename: string): iterator(): Record =
  return iterator(): Record =
    for rec in readSeqs(filename):
      yield rec
Andrea T.
  • 920
  • 4
  • 15
pietroppeter
  • 1,433
  • 13
  • 30
  • 1
    Many thanks for the help. What I don't get at the moment is how to use `zip` with closures requiring a parameter. I tried with your closureReadSeqs but probaly my error is in how i use zip. I tried extending your example: https://gist.github.com/telatin/3e37d356937b1b2155b50b546ebb8982 – Andrea T. Sep 28 '20 at 09:22
  • You are right, the approach proposed would not work with iterators requiring a parameter. iterator semantics is kind of tricky (see e.g. for another example: https://stackoverflow.com/questions/63884552/iterator-producing-function-in-nim-works-when-assigning-the-iterator-stuck-whe/63906287#63906287). I updated my answer and the extended example now works (I used the approach mentioned here: https://nim-by-example.github.io/for_iterators/). Let me know if this helps also for nimbioseq. – pietroppeter Sep 28 '20 at 10:44
  • 1
    also, since iterators and procs in nim have different namespaces (a somewhat deprecated feature, but probably here to stay for a while: https://github.com/nim-lang/Nim/issues/8901), you should be able to name the above proc readSeqs and it should work fine. – pietroppeter Sep 28 '20 at 10:48
  • 1
    Hi @Andrea T. I approved the edit to python syntax highlighting since it is currently the best option to have some syntax highlighting with Nim. Still I am hoping that soon we will have native syntax highlighting. They recently changed the highlighting engine from prettify to highlight.js, which does support Nim. As far as I understand they are still keeping the same scope of languages of prettify in this first phase of the migration, but I expect that Nim will be supported soon enough. https://meta.stackexchange.com/questions/184108/what-is-syntax-highlighting-and-how-does-it-work/184109 – pietroppeter Oct 17 '20 at 07:53