如何在不进行for循环的情况下将任意数量的参数传递给R函数?

时间:2018-08-10 21:15:49

标签: r

我的问题是在保留代码功能的同时摆脱for循环。

我有一个元素A_1, A_2, ... A_N的成对排序矩阵。每个排序都表示为矩阵的一行。下面的代码显示了一个示例。

# Matrix representing the relations
# A1 < A2, A1 < A5, A2 < A4
(mat <- matrix(c(1, 2, 1, 5, 2, 4), ncol = 2, byrow = TRUE))
#>      [,1] [,2]
#> [1,]    1    2
#> [2,]    1    5
#> [3,]    2    4

我希望将整个矩阵作为一组有序对。原因是我以后需要生成这些关系的传递性关闭。我一直在使用sets包并在下面创建函数。

create_sets <- function(mat){
  # Empty set
  my_set <- sets::set()

  # For loop for adding pair elements to the set, one at a time
  for(i in seq(from = 1, to = nrow(mat), by = 1)){
    my_set <- sets::set_union(my_set, 
                              sets::pair(mat[[i, 1]], mat[[i, 2]]))
  }

  return(my_set)
}

create_sets(mat)
#> {(1, 2), (1, 5), (2, 4)}

此功能运行良好,但是我认为for循环是不必要的,并且不能替换它。对于上面只有三行的特定示例矩阵,我可以改用以下代码:

my_set2 <- sets::set(
                sets::pair(mat[[1, 1]], mat[[1, 2]]), 
                sets::pair(mat[[2, 1]], mat[[2, 2]]),
                sets::pair(mat[[3, 1]], mat[[3, 2]])
)

my_set2
#> {(1, 2), (1, 5), (2, 4)}

之所以可行,是因为sets::set可以有任意数量的对。

args(sets::set)
#> function (...) 
#> NULL

但是,矩阵mat将具有任意数量的行,我希望该函数能够处理所有可能的情况。这就是为什么我无法摆脱for循环的原因。

因此,我的问题是:给定一个矩阵mat,其中每行代表一个有序对,是否有一些通用方法将每行中的对作为单独的参数传递给sets::set,而不会循环?

2 个答案:

答案 0 :(得分:1)

选项1:不执行任何操作

for循环并不总是世界末日,如果矩阵不是很大,这看起来也不是很糟糕。

选项2:拆分,应用,合并方式(通过新功能)

编写一个将行内容组合在一起的函数(有一种较短的方法,但这使您的任务明确)

f <- function(x) {
  sets::pair(x[1], x[2])
}

Reduce(sets::set_union, lapply(split(mat, 1:nrow(mat)), f))
## {(1, 2), (1, 5), (2, 4)}

Reduce的作用与for循环相同(重复应用set_union),lapply将矩阵变成成对的列表(也像for循环一样)

答案 1 :(得分:1)

OP询问了

  

[...]是否有一些通用的方法将每一行中的对作为单独的参数传递给sets::set,而不会循环?

是的,do.call()函数可能正是您想要的。来自help(do.call)

  

do.call从名称或函数以及要传递给它的参数列表中构造并执行函数调用。

因此,OP的create_sets()函数可以替换为

do.call(sets::set, apply(mat, 1, function(x) sets::pair(x[1], x[2])))
{(1, 2), (1, 5), (2, 4)}

do.call()的第二个参数需要一个列表。这是由

创建的
apply(mat, 1, function(x) sets::pair(x[1], x[2]))

返回列表

[[1]]
(1, 2)

[[2]]
(1, 5)

[[3]]
(2, 4)

apply(mat, 1, FUN)是一种隐式for循环,它循环调用矩阵mat的行,并在调用函数FUN时将行值的向量作为参数。

编辑:as.tuple()代替pair()

pair()函数需要两个参数。这就是为什么我们被迫定义一个匿名函数function(x) sets::pair(x[1], x[2])

as.tuple()函数将对象的元素强制为集合的元素。因此,代码可以更加简化:

do.call(sets::set, apply(mat, 1, sets::as.tuple))
{(1, 2), (1, 5), (2, 4)}

在这里,as.tuple()提取行值的整个向量并将其强制为一组。