custom iterators and foreach inside package - fails
1
0
Entering edit mode
ben.ward ▴ 30
@benward-7169
Last seen 8.1 years ago
United Kingdom

Hi, hopefully someone out there is good with foreach and iterators! I've been struggling with this:

 

I have created a custom iterator, inheriting from there iter class in the iterators package. The iterator and its methods are not exported from the package. Here is the iterator and a test function in a script that is reproducible and runnable, the iterator is called pairsRef:

library(Biostrings)
library(iterators)
library(foreach)    

setGeneric("maskSequences", function(object, seqnames, invert = TRUE, append = "union"){
  standardGeneric("maskSequences")
})

setMethod("maskSequences",
          signature("DNAMultipleAlignment", "character", "logical", "character"),
          function(object, seqnames, invert, append) {
            sequenceNames <- rownames(object)
            rowmask(object, append = append, invert = invert) <-
              which(sequenceNames %in% seqnames)
            return(object)
          })

pairsRef <- function(obj, ...){
  UseMethod('pairsRef')
}

pairsRef.DNAMultipleAlignment <-
  function(obj, ref = NULL, checkFun = function(x, ...) TRUE){
    state <- new.env()
    state$i <- 0L
    state$obj <- obj
    if(is.null(ref)){
      state$ref <- rownames(obj)[1]
    } else {
      state$ref <- ref
    }
    state$nonRefs <- rownames(obj)
    state$nonRefs <- state$nonRefs[state$nonRefs != state$ref]
    it <- list(state=state, checkFun = checkFun)
    class(it) <- c("pairsRef", "abstractiter", "iter")
    return(it)
  }

nextElem.pairsRef <- function(obj, ...){
  repeat {
    obj$state$i <- obj$state$i + 1L
    if(obj$state$i > length(obj$state$nonRefs))
      stop('StopIteration', call.=FALSE)
    pair <- maskSequences(obj$state$obj,
                          c(obj$state$ref, obj$state$nonRefs[obj$state$i]),
                          invert = TRUE,
                          append = "replace"
    )
    if(obj$checkFun(pair)){
      return(pair)
    }
  }
}

Test2 <- function(dna, ref){
  pit <- pairsRef(dna, ref = ref, checkFun = function(x) TRUE)
  results <- foreach(x = pit, .combine = c, .multicombine = TRUE) %do% {x}
  return(results)
}

dna <-
  readDNAMultipleAlignment(filepath =
                             system.file("extdata",
                                         "msx2_mRNA.aln",
                                         package="Biostrings"),
                           format="clustal")

Test2(dna, rownames(dna)[1])

 

However, I want to use this iterator to do foreach loops in a package of mine. If I put Test2 in a package (exported), and I have all the other functions in the package (unexported), and I have the package namespace import Biostrings, iterators, and foreach. It does not work. With a fresh R session, loading the package, and the running:

 

dna <-
  Biostrings::readDNAMultipleAlignment(filepath =
                             system.file("extdata",
                                         "msx2_mRNA.aln",
                                         package="Biostrings"),
                           format="clustal")

Test2(dna, rownames(dna)[1])

 

Is this because the custom iterator is internal to the package? Any help or suggestions are greatly appreciated.

 

If I export the iterator and it's functions from the package. Then all works fine. But I don't necessarily want to export iterators of the package.

 

I have tried to look at the code for `%do%` in foreach, and have found `doSEQ` has.... environmenty stuff ... it gets called with parent.frame() and such on.

Has anyone else used custom iterators and foreach in their packages? How did you do it?

Many thanks,

Ben.

foreach iterators • 1.4k views
ADD COMMENT
1
Entering edit mode
ben.ward ▴ 30
@benward-7169
Last seen 8.1 years ago
United Kingdom

I've managed to solve this.

In order for foreach to work inside the package with non-exported iterators, the method nextElem must be imported from iterators, and then the additional method unique to the package, exported, such that it is visible to the functions in the foreach package namespace.

ADD COMMENT
0
Entering edit mode

A test for your solution would be to ensure that the package works when it is loaded but not attached, for instance if the iterator were use in  a function foo() then invoke foo without it being on the search path, in a new R session yourpkg::foo() without first saying library(foo). This is how a package that Imports: your package would see foo().

ADD REPLY

Login before adding your answer.

Traffic: 802 users visited in the last hour
Help About
FAQ
Access RSS
API
Stats

Use of this site constitutes acceptance of our User Agreement and Privacy Policy.

Powered by the version 2.3.6