Skip to content

Commit

Permalink
Draft before release
Browse files Browse the repository at this point in the history
  • Loading branch information
gdemin committed Feb 17, 2019
1 parent e96e9de commit cb8eaa7
Show file tree
Hide file tree
Showing 20 changed files with 892 additions and 125 deletions.
4 changes: 4 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
^Meta$
^doc$
^.*\.Rproj$
^\.Rproj\.user$
SupplementaryMaterials
README.md
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
Meta
doc
.Rproj.user
.Rhistory
.RData
.Ruserdata
SupplementaryMaterials

25 changes: 18 additions & 7 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
Package: comprehenr
Type: Package
Title: What the Package Does (Title Case)
Version: 0.1.0
Author: Who wrote it
Maintainer: The package maintainer <[email protected]>
Description: More about what it does (maybe more than one line)
Use four spaces when indenting paragraphs within the Description.
License: What license is it under?
Title: List comprehensions in R
Version: 0.5.0
Maintainer: Gregory Demin <[email protected]>
Authors@R: person("Gregory", "Demin", email = "[email protected]",
role = c("aut", "cre"))
Description: Package provides 'Python'-style list comprehensions for R.
It uses ususal R loops ('for', 'while' and 'repeat') and usual 'if' as list producers.
Simple example: 'to_list(for(i in 1:10) if(i %% 2==0) i*i)'.
URL: https://github.com/gdemin/comprehenr
BugReports: https://github.com/gdemin/comprehenr/issues
Depends:
R (>= 3.3.0),
Suggests:
knitr,
testthat,
VignetteBuilder: knitr
License: GPL-2
Encoding: UTF-8
LazyData: true
RoxygenNote: 6.1.1
12 changes: 11 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
exportPattern("^[[:alpha:]]+")
# Generated by roxygen2: do not edit by hand

export(enumerate)
export(mark)
export(numerate)
export(to_list)
export(to_vec)
export(unmark)
export(unnumerate)
export(unzip_list)
export(zip_lists)
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
0.5 (19.02.2019)
================
* Initial release
56 changes: 0 additions & 56 deletions R/enumerate.R

This file was deleted.

18 changes: 0 additions & 18 deletions R/increment.R

This file was deleted.

101 changes: 101 additions & 0 deletions R/numerate.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#' Auxiliary functions for working with lists
#'
#' \itemize{
#' \item{numerate}{ returns list of lists. Each list consists of two elements:
#' sequential number of element and element. Reverse operation -
#' \code{unumerate}.}
#' \item{mark}{ returns list of lists. Each list consists of two elements: name of element and element. Reverse operation -
#' \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.}
#' }
#'
#' @param x list or list of lists
#' @param item numeric number of list in which stored values
#' @param ... lists which will be zipped
#'
#' @return list or list of lists
#' @export
#'
#' @examples
#' cities = c('Chicago', 'Detroit', 'Atlanta')
#' airports = c('ORD', 'DTW', 'ATL')
#' pairs = zip_lists(cities, airports)
#'
#' str(pairs)
#' str(unzip_list(pairs))
#'
#' str(numerate(cities))
#'
#' named_list = c('Chicago' = 'ORD', 'Detroit' = 'DTW', 'Atlanta' = 'ATL')
#' str(mark(named_list))
#'
numerate = function(x){
unzip_list(list(seq_along(x), x))
}

#' @export
#' @rdname numerate
enumerate = numerate

#' @export
#' @rdname numerate
unnumerate = function(x, item = 2){
unzip_list(x)[[item]]
}

#' @export
#' @rdname numerate
mark = function(x){
all_names = names(x)
if(is.null(all_names)) all_names = rep("", length(x))
unzip_list(list(all_names, x))

}


#' @export
#' @rdname numerate
unmark = function(x, item = 2){
res = unzip_list(x)
set_names(
res[[item]],
unlist(res[[1]], use.names = FALSE)
)
}


#' @export
#' @rdname numerate
unzip_list = function(x){
all_lengths = unique(lengths(x))
if(length(x)==0) return(x)
if(length(all_lengths)!=1){
stop("each element of the argument 'x' should have the same length.")
}
global_names = names(x)
local_names = lapply(x, names)
get_names = function(i){
res = lapply(local_names, "[[", i)
all_lengths = lengths(res)
if(all(all_lengths==0)) {
return(global_names)
}
res[all_lengths==0] = ""
unlist(res, use.names = FALSE)

}
lapply(seq_along(x[[1]]), function(i) set_names(lapply(x, `[[`, i), get_names(i)))
}

#' @export
#' @rdname numerate
zip_lists = function(...){
unzip_list(list(...))
}


set_names = function (object = nm, nm) {
names(object) = nm
object
}
83 changes: 62 additions & 21 deletions R/to_list.R
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
#' List comprehensions for R
#'
#' @param expr expression which starts with \code{for} or \code{while}
#' \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.
#' @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.
#' @return list for \code{to_list} and vector for \code{to_vec}
#' @export
#'
#' @examples
#' # rather useless expression - squares of even numbers
#' to_list(for(i in 1:10) if(i %% 2==0) i*i)
#'
#' # Pythagorean triples
#' to_list(for (x in 1:30) for (y in x:30) for (z in y:30) if (x^2 + y^2 == z^2) c(x, y, z))
#'
#' colours = c("red", "green", "yellow", "blue")
#' things = c("house", "car", "tree")
#' to_vec(for(x in colours) for(y in things) paste(x, y))
#'
#' # prime numbers
#' 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_list = function(expr){
expr = substitute(expr)
if(!is.call(expr)) {
stop(paste("to_list: argument should be expression with 'for' or 'while' but we have: ", deparse(expr, width.cutoff = 500)[1]))
}
first_item = expr[[1]]
if(!(
identical(first_item, quote(`for`)) ||
identical(first_item, quote(`while`))
)
) {
stop(paste("to_list: argument should be expression with 'for' or 'while' but we have: ", deparse(expr, width.cutoff = 500)[1]))
if(!is_loop(expr)) {
stop(paste("argument should be expression with 'for', 'while' or 'repeat' but we have: ", deparse(expr, width.cutoff = 500)[1]))
}
last_item = length(expr)
expr[[last_item]] = bquote({

.__curr = {.(expr[[last_item]])}
if(!is.null(.__curr)){
.___counter = .___counter + 1
.___res[[.___counter]] = .__curr
}

})
expr = add_assignment_to_final_loops(expr)
on.exit(suppressWarnings(rm(list = c(".___res", ".___counter", ".__curr"), envir = parent.frame())))
eval.parent(quote(.___res <- list()))
eval.parent(quote(.___counter <- 0)) # initial list length
Expand All @@ -46,10 +48,49 @@ to_vec = function(expr, recursive = TRUE, use.names = FALSE){
}


add_assignment_to_final_loops = function(expr){
if(is_loop(expr)){
if(has_loop_inside(expr[-1])){
expr[-1] = as.call(lapply(as.list(expr)[-1], add_assignment_to_final_loops))
} else {
expr = add_assignment_to_loop(expr)
}
} else {
if(has_loop_inside(expr)){
expr = as.call(lapply(as.list(expr), add_assignment_to_final_loops))
}
}
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`))
}

has_loop_inside = function(expr){
if(!is.call(expr)) return(FALSE)
if(is_loop(expr)) return(TRUE)
any(
unlist(
lapply(as.list(expr), has_loop_inside),
recursive = TRUE,
use.names = FALSE
))
}


add_assignment_to_loop = function(expr){
last_item = length(expr)
expr[[last_item]] = bquote({

.__curr = {.(expr[[last_item]])}
if(!is.null(.__curr)){
.___counter = .___counter + 1
.___res[[.___counter]] = .__curr
}


})
expr
}
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# List comprehensions in R

Package provides 'Python'-style list comprehensions for R. It uses ususal R loops (`for`, `while` and `repeat`) and usual `if` as list producers. Syntax is very similiar to Python. The diffrenece is that returned value shoud be at the end of the loop body.

```R
# rather useless statement - squares of even numbers
to_list(for(i in 1:10) if(i %% 2==0) i*i)

# Pythagorean triples
to_list(for (x in 1:20) for (y in x:20) for (z in y:20) if (x^2 + y^2 == z^2) c(x, y, z))

colours = c("red", "green", "yellow", "blue")
things = c("house", "car", "tree")
to_vec(for(x in colours) for(y in things) paste(x, y))

# prime numbers
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
```
1 change: 1 addition & 0 deletions comprehenr.Rproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ StripTrailingWhitespace: Yes
BuildType: Package
PackageUseDevtools: Yes
PackageInstallArgs: --no-multiarch --with-keep.source
PackageRoxygenize: rd,collate,namespace,vignette
12 changes: 0 additions & 12 deletions man/hello.Rd

This file was deleted.

Loading

0 comments on commit cb8eaa7

Please sign in to comment.