Skip to content

Commit

Permalink
Add iterations over multiple lists and 'lag_list' function
Browse files Browse the repository at this point in the history
  • Loading branch information
gdemin committed Mar 17, 2019
1 parent 5f4c2fa commit 4682121
Show file tree
Hide file tree
Showing 13 changed files with 158 additions and 8 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: comprehenr
Type: Package
Title: List Comprehensions
Version: 0.5.4
Version: 0.6.0
Maintainer: Gregory Demin <gdemin@gmail.com>
Authors@R: person("Gregory", "Demin", email = "gdemin@gmail.com",
role = c("aut", "cre"))
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by roxygen2: do not edit by hand

export(enumerate)
export(lag_list)
export(mark)
export(numerate)
export(to_list)
Expand Down
5 changes: 5 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
0.6.0 (18.03.2019)
================
* add iterations over multiple lists: to_vec(for(`i, j` in numerate(letters)) if(i %% 2==0) paste(i, j))
* add function 'lag_list': x -> list(x[i-1], x[i])

0.5.4 (06.03.2019)
================
* Initial release
13 changes: 12 additions & 1 deletion R/numerate.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
#' \code{unmark}.}
#' \item{zip_lists}{ combine lists side-by-sidy. Reverse operation - \code{unzip_list}.}
#' \item{unzip_list}{ It's similair to matrix transposition but for list of lists.}
#' \item{lag_list}{ convert argument to list of arguments with previous values. x -> list(x[i-1], x[i]).}
#' }
#'
#' @param x list or list of lists
#' @param x list, vector or list of lists
#' @param item numeric number of list in which stored values
#' @param ... lists which will be zipped
#'
Expand All @@ -30,6 +31,10 @@
#' named_list = c('Chicago' = 'ORD', 'Detroit' = 'DTW', 'Atlanta' = 'ATL')
#' str(mark(named_list))
#'
#' set.seed(123)
#' rand_sequence = runif(20)
#' # gives only locally increasing values
#' to_vec(for(`i, j` in lag_list(rand_sequence)) if(j>i) j)
numerate = function(x){
unzip_list(list(seq_along(x), x))
}
Expand Down Expand Up @@ -94,6 +99,12 @@ zip_lists = function(...){
unzip_list(list(...))
}

#' @export
#' @rdname numerate
lag_list = function(x){
lapply(seq_along(x)[-1], function(i) list(x[i-1], x[i]))
}


set_names = function (object = nm, nm) {
names(object) = nm
Expand Down
48 changes: 47 additions & 1 deletion R/to_list.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
#'
#' \code{to_list} converts usual R loops expressions to list producers.
#' Expression should be started with \code{for} , \code{while} or
#' \code{repeat}. See examples.
#' \code{repeat}. You can iterate over multiple lists if you provide several
#' loop variables in backticks. See examples.
#' @param expr expression which starts with \code{for} , \code{while} or \code{repeat}.
#' @param recursive logical. Should unlisting be applied to list components of result? See \link[base]{unlist} for details.
#' @param use.names logical. Should names be preserved? See \link[base]{unlist} for details.
Expand All @@ -24,12 +25,21 @@
#' noprimes = to_vec(for (i in 2:7) for (j in seq(i*2, 99, i)) j)
#' primes = to_vec(for (x in 2:99) if(!x %in% noprimes) x)
#' primes
#'
#' # iteration over multiple lists
#' to_vec(for(`i, j` in numerate(letters)) if(i %% 2==0) paste(i, j))
#'
#' set.seed(123)
#' rand_sequence = runif(20)
#' # gives only locally increasing values
#' to_vec(for(`i, j` in lag_list(rand_sequence)) if(j>i) j)
to_list = function(expr){
expr = substitute(expr)
if(!is_loop(expr)) {
stop(paste("argument should be expression with 'for', 'while' or 'repeat' but we have: ", deparse(expr, width.cutoff = 500)[1]))
}

expr = expand_loop_variables(expr)
expr = add_assignment_to_final_loops(expr)
on.exit(suppressWarnings(rm(list = c(".___res", ".___counter", ".__curr"), envir = parent.frame())))
eval.parent(quote(.___res <- list()))
Expand Down Expand Up @@ -63,12 +73,48 @@ add_assignment_to_final_loops = function(expr){
expr
}

expand_loop_variables = function(expr){
if(!is.call(expr)) return(expr)
if(is_for_loop(expr)){
expr = add_expansion_to_loop(expr)
expr[-1] = as.call(lapply(as.list(expr)[-1], expand_loop_variables))
} else {
expr = as.call(lapply(as.list(expr), expand_loop_variables))
}
expr
}

add_expansion_to_loop = function(expr){

loop_variable = expr[[2]]
if(is.symbol(loop_variable) && grepl(",", as.character(loop_variable))){
all_loop_variables = trimws(unlist(strsplit(as.character(loop_variable), split = ",")))
expansion_expr = lapply(seq_along(all_loop_variables), function(item_num){
curr_item = as.name(all_loop_variables[item_num])
bquote(.(curr_item) <- .(loop_variable)[[.(item_num)]])
})
expansion_expr = as.call(c(list(quote(`{`)), expansion_expr))
last_item = length(expr)
expr[[last_item]] = bquote({
.(expansion_expr)
.(expr[[last_item]])
})
}
expr
}

is_loop = function(expr){
if(!is.call(expr)) return(FALSE)
first_item = expr[[1]]
identical(first_item, quote(`for`)) ||identical(first_item, quote(`while`)) ||identical(first_item, quote(`repeat`))
}

is_for_loop = function(expr){
if(!is.call(expr)) return(FALSE)
first_item = expr[[1]]
identical(first_item, quote(`for`))
}

has_loop_inside = function(expr){
if(!is.call(expr)) return(FALSE)
if(is_loop(expr)) return(TRUE)
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,13 @@ noprimes = to_vec(for (i in 2:7) for (j in seq(i*2, 99, i)) j)
primes = to_vec(for (x in 2:99) if(!x %in% noprimes) x)
primes
```
You can iterate over multiple lists if you provide several loop variables in backticks:
```R
to_vec(for(`i, j` in numerate(letters)) if(i %% 2==0) paste(i, j))

set.seed(123)
rand_sequence = runif(20)
# gives only locally increasing values
to_vec(for(`i, j` in lag_list(rand_sequence)) if(j>i) j)

```
10 changes: 9 additions & 1 deletion man/numerate.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion man/to_list.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions tests/testthat/test_numerate.R
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,14 @@ expect_error(
)
)
)



expect_equal(
lag_list(1:3),
list(
list(1,2),
list(2,3)
)

)
18 changes: 18 additions & 0 deletions tests/testthat/test_to_list.R
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,21 @@ expect_equal(
to_vec(for(i in colnames(iris)) if(is.numeric(iris[[i]])) setNames(mean(iris[[i]]), i) , use.names = TRUE),
colMeans(iris[,-5])
)


context("`i, j`")

res = to_vec(for(`i, j` in zip_lists(letters, LETTERS)) paste(i, j))
expect_identical(res, paste(letters, LETTERS))


res = to_vec(for(`i, j` in numerate(letters)) if(i %% 2==0) paste(i, j))
index = seq_along(letters)
expect_identical(res, paste(index[index %% 2==0], letters[index %% 2==0]))


res = to_vec(for(`i, j` in numerate(letters)) if(i %% 2==0) for(`k, z` in numerate(LETTERS)) paste(i, j, k, z))
index = seq_along(letters)
true_res = expand.grid(paste(seq_along(letters), LETTERS), paste(index[index %% 2==0], letters[index %% 2==0]))
true_res = paste(true_res[[2]], true_res[[1]])
expect_identical(res, true_res)
9 changes: 9 additions & 0 deletions vignettes/Introduction.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,12 @@ noprimes = to_vec(for (i in 2:7) for (j in seq(i*2, 99, i)) j)
primes = to_vec(for (x in 2:99) if(!x %in% noprimes) x)
primes

## ------------------------------------------------------------------------
to_vec(for(`i, j` in numerate(letters)) if(i %% 2==0) paste(i, j))

set.seed(123)
rand_sequence = runif(20)
# gives only locally increasing values
to_vec(for(`i, j` in lag_list(rand_sequence)) if(j>i) j)


15 changes: 13 additions & 2 deletions vignettes/Introduction.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@



<title>List comprehensions in R</title>
<title>List Comprehensions in R</title>



Expand Down Expand Up @@ -296,7 +296,7 @@



<h1 class="title toc-ignore">List comprehensions in R</h1>
<h1 class="title toc-ignore">List Comprehensions in R</h1>



Expand Down Expand Up @@ -338,6 +338,17 @@ <h1 class="title toc-ignore">List comprehensions in R</h1>
<a class="sourceLine" id="cb3-11" data-line-number="11">primes</a>
<a class="sourceLine" id="cb3-12" data-line-number="12"><span class="co">#&gt; [1] 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83</span></a>
<a class="sourceLine" id="cb3-13" data-line-number="13"><span class="co">#&gt; [24] 89 97</span></a></code></pre></div>
<p>You can iterate over multiple lists if you provide several loop variables in backticks:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode r"><code class="sourceCode r"><a class="sourceLine" id="cb4-1" data-line-number="1"><span class="kw">to_vec</span>(<span class="cf">for</span>(<span class="st">`</span><span class="dt">i, j</span><span class="st">`</span> <span class="cf">in</span> <span class="kw">numerate</span>(letters)) <span class="cf">if</span>(i <span class="op">%%</span><span class="st"> </span><span class="dv">2</span><span class="op">==</span><span class="dv">0</span>) <span class="kw">paste</span>(i, j))</a>
<a class="sourceLine" id="cb4-2" data-line-number="2"><span class="co">#&gt; [1] &quot;2 b&quot; &quot;4 d&quot; &quot;6 f&quot; &quot;8 h&quot; &quot;10 j&quot; &quot;12 l&quot; &quot;14 n&quot; &quot;16 p&quot; &quot;18 r&quot; &quot;20 t&quot;</span></a>
<a class="sourceLine" id="cb4-3" data-line-number="3"><span class="co">#&gt; [11] &quot;22 v&quot; &quot;24 x&quot; &quot;26 z&quot;</span></a>
<a class="sourceLine" id="cb4-4" data-line-number="4"></a>
<a class="sourceLine" id="cb4-5" data-line-number="5"><span class="kw">set.seed</span>(<span class="dv">123</span>)</a>
<a class="sourceLine" id="cb4-6" data-line-number="6">rand_sequence =<span class="st"> </span><span class="kw">runif</span>(<span class="dv">20</span>)</a>
<a class="sourceLine" id="cb4-7" data-line-number="7"><span class="co"># gives only locally increasing values</span></a>
<a class="sourceLine" id="cb4-8" data-line-number="8"><span class="kw">to_vec</span>(<span class="cf">for</span>(<span class="st">`</span><span class="dt">i, j</span><span class="st">`</span> <span class="cf">in</span> <span class="kw">lag_list</span>(rand_sequence)) <span class="cf">if</span>(j<span class="op">&gt;</span>i) j)</a>
<a class="sourceLine" id="cb4-9" data-line-number="9"><span class="co">#&gt; [1] 0.7883051 0.8830174 0.9404673 0.5281055 0.8924190 0.9568333 0.6775706</span></a>
<a class="sourceLine" id="cb4-10" data-line-number="10"><span class="co">#&gt; [8] 0.8998250 0.3279207 0.9545036</span></a></code></pre></div>



Expand Down
13 changes: 12 additions & 1 deletion vignettes/Introduction.rmd
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: "List comprehensions in R"
title: "List Comprehensions in R"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{List comprehensions in R}
Expand Down Expand Up @@ -39,3 +39,14 @@ noprimes = to_vec(for (i in 2:7) for (j in seq(i*2, 99, i)) j)
primes = to_vec(for (x in 2:99) if(!x %in% noprimes) x)
primes
```

You can iterate over multiple lists if you provide several loop variables in backticks:
```{r}
to_vec(for(`i, j` in numerate(letters)) if(i %% 2==0) paste(i, j))
set.seed(123)
rand_sequence = runif(20)
# gives only locally increasing values
to_vec(for(`i, j` in lag_list(rand_sequence)) if(j>i) j)
```

0 comments on commit 4682121

Please sign in to comment.