【问题标题】:Why are For Loops Not Effective in R and Looping Through Indices为什么 For 循环在 R 和循环索引中无效
【发布时间】:2020-08-25 15:40:31
【问题描述】:

我正在 R-bloggers 网站上进行 R 练习。这是我正在解决的问题:

如果给定的整数在向量内,则创建一个返回 TRUE 的函数。

提供的解决方案代码为:

f.exists <- function (v, x) {
  exist <- FALSE
  i <- 1

  while (i <= length (v) & !exist) {

    if (v[i] == x) {
      exist <- TRUE
    }
  i <- 1 + i
  }
  exist
}

但是,我的尝试是:

isInside <- function(x, y) {
  i <- 1
  for (i in x) {
    print(i)
    if (x[i] == y) {
      return(TRUE)
    }
    else {
      return(FALSE)
    }
  }
} 

test <- c(1,2,3,4,5,6,7,8,9,10,10.25,100)
isInside(test,10.25)

这会返回[1] 1 [1] False,这意味着它只循环了一次,一旦命中假就退出。但它应该返回 true,因为 10.25 在向量 x 中。我不确定为什么if 语句不起作用,因为它应该循环遍历x 的每个索引以查看数字是否在向量中。

另外,我在this post 上发现,说你应该使用while 循环而不是for 循环。为什么for 循环如此糟糕的做法?我的for 循环是否存在固有问题使其无法正常工作?

【问题讨论】:

  • (你是否意识到return 突破了任何forwhilerepeat 或其他任何东西?它会立即转义函数。)
  • (很抱歉将新的尝试粘贴为评论,认为将其放在我的原始帖子中不合适。)@r2evans
  • 如果您想在找到匹配项时返回 true,那么可能只需要 if (x[i] == y) return(TRUE) 而在 x[i] != y 时什么也不做。这样,循环将进入下一次迭代并重试。循环完成后,您可能应该return(FALSE),因为代码执行在函数中到达该点的唯一方法是比较整个向量并且没有任何内容导致立即返回。
  • 另外,ix 的一个元素,而不是它的索引,所以x[i] 是不合适的。 (并且没有必要预先实例化 i &lt;- 1,因为 for 循环会为您处理这个问题。)
  • 也许是function(x, y) { for (el in x) if (el == y) return(TRUE) ; return(FALSE); }。 (不过,这个讨论完全是学术性的,因为希望有人更喜欢使用any(x %in% y)。)

标签: r


【解决方案1】:

避开首选使用 R 的 %in% 内联运算符,让我们分析一下您的函数。

  1. for (i in x) 正在逐步检查x 的每个。这意味着在第 11 次通过这样的循环(没有中断)时,i 将是 10.25 而不是 11。当您引用 x[i] 时,这将在逻辑上失败:它不会抛出错误(老实说,我认为这是 R 的失败),但它没有任何意义(@987654328 是什么@?x[100] 的下一次传递应该返回什么?)。这应该是for (i in seq_along(x)) 或坚持使用for (i in x),然后将x[i] == y 替换为i == y

  2. 找到匹配项时选择return(TRUE)return 函数立即跳出for 循环并跳出函数,返回值TRUE。如果没有找到匹配,你return(FALSE),它也立即跳出for循环和函数,返回值FALSE。我认为你的意图是让循环继续,所以if 语句的else 子句是......不必要的。如果你去掉else 语句,也许效果会更好。

  3. Minor,你预定义了i &lt;- 1。这不会造成任何伤害或改变,但在 R 中是完全没有必要的。i 不会在循环之外被引用,并且一旦for 开始,i 就会被分配x 的第一个值。因此,您可以安全地删除 i &lt;- 1 并且执行应该不受影响。 (这不是代码风格,只是不必要的代码。)

一种方法的演示:

myany <- function(x, y) {
  for (el in x) {
    if (el == y) {
      return(TRUE)
    }
  }
  return(FALSE)
}
test <- c(1,2,3,4,5,6,7,8,9,10,10.25,100)
myany(test, 10.25)
# [1] TRUE

或者,

myany <- function(x, y) {
  for (ind in seq_along(x)) {
    if (x[ind] == y) {
      return(TRUE)
    }
  }
  return(FALSE)
}

我们可以在这个例子中看到一些错误:

myany <- function(x, y) {
  for (i in x) {
    message("Comparing ", sQuote(i), " (which is ", sQuote(x[i]), ") with ", sQuote(y))
    if (x[i] == y) {
      return(TRUE)
    }
  }
  return(FALSE)
}

myany(test, 10.25)
# Comparing '1' (which is '1') with '10.25'
# Comparing '2' (which is '2') with '10.25'
# Comparing '3' (which is '3') with '10.25'
# Comparing '4' (which is '4') with '10.25'
# Comparing '5' (which is '5') with '10.25'
# Comparing '6' (which is '6') with '10.25'
# Comparing '7' (which is '7') with '10.25'
# Comparing '8' (which is '8') with '10.25'
# Comparing '9' (which is '9') with '10.25'
# Comparing '10' (which is '10') with '10.25'
# Comparing '10.25' (which is '10') with '10.25'
# Comparing '100' (which is 'NA') with '10.25'
# Error in if (x[i] == y) { : missing value where TRUE/FALSE needed

这是我们看到正在发生的事情的地方。当i10 时,它看起来很好,因为test 的第10 个元素确实是10。但是,在下一次传递中,i10.25,而不是您预期的11。 R 默默地将x[10.25] 截断为x[10],这就是为什么它说which is '10',因为第10 个元素确实是10。下一次是i100,而test[100] 显然不存在.在 R 中,当您尝试检索超出向量定义长度的索引时,它将返回 NA(当您尝试此操作时,其他语言会给出错误或核心转储)。

【讨论】:

  • 我的想法是i 每次都会增加1,所以x[11]= 10.25。所以在这种情况下,如果y = 10.25 那么x[11] = 10.25 = y。同样x[12]= 100。所以i 只能通过x 的长度,正如你提到的,我应该使用seq_along(x),或者length(x)
  • 我知道你是这么想的,所以我建议使用for (i in seq_along(x))。但是i 在您的代码中永远不会是 11,因为您被分配给 x 的每个 valuei,而不是每个 index x.
  • 谢谢,这对我理解 R 非常有帮助。最后一个小问题是关于您的注释代码中的;; 是否只是代表代码中的一个中断,因为您无法在 cmets 中格式化,所以我能够阅读它?还是在这种情况下有其他目的
  • ; 的使用只是为了在注释中显示一些有用的代码。在 R 中,; 不是必需的,除非您将两个或多个表达式放在一行中(通常在源文件中使用换行符)。在 StackOverflow 上,我们不能很好地表示换行符,所以我使用 ; 以便代码可以实际使用。如果将其键入源文件并用换行符替换;,它可能看起来更合适。 (如果您这样做,还要在 { 之后添加换行符。)
  • 是的,这就是我所做的,但我只是想确保这就是它的意图。再次感谢!
【解决方案2】:

这里有几个问题。正如 r2evans 所指出的,由于您的代码,您的循环在第一次迭代时退出。它必须在第一次迭代时返回 TRUE 或 FALSE。 此外,由于一个简单的错误,您的循环将无法工作:for (i in x) 无法工作,因为x 应该评估为整数向量。您正在寻找:for (i in 1:length(x))

> for (i in test) {print(i)}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 10.25
[1] 100

在第 11 次迭代中,R 将 10.25 解释为 10,所以你很幸运。在第 12 次循环中,代码将在 test 中查找第 100 个元素,并返回 NA

通过使用某些 R 函数的固有向量化,有一个更简单的解决方案,无需循环:

    test <- c(1,2,3,4,5,6,7,8,9,10,10.25,100)
    x <- 10.25
    any(test == x)

将返回:

> any(test == x)
[1] TRUE

如果你想知道哪个元素:

> which(test == x)
[1] 11

作为一个函数:

isInside <- function(x, v) return(any(x == v))
isInside(test, 10.25)

返回:

> isInside(test, 10.25)
[1] TRUE
> isInside(test, 11)
[1] FALSE

【讨论】:

  • 我建议seq_along(x) 而不是1:length(x)。例如为什么,考虑x &lt;- c():人们可能期望1:length(x)nothing(返回一个长度为0的向量),但它解析为1:0,它返回一个长度为的向量2(而不是与x 长度相同的向量)。或者,seq_lenseq_along 都可以正确处理零长度向量。
猜你喜欢
  • 2019-12-23
  • 2022-01-22
  • 2021-12-08
  • 2015-01-19
  • 1970-01-01
  • 2017-09-21
  • 1970-01-01
  • 2012-09-02
  • 2016-09-28
相关资源
最近更新 更多