与purrr::map中的'break'等效

与purrr::map中的'break'等效,r,dplyr,purrr,R,Dplyr,Purrr,假设我想运行一个循环,直到满足一个条件,此时结果被保存,循环退出: library(tidyverse) for (i in 1:5) { df <- iris %>% select(i) %>% head(2) if (names(df) == "Petal.Width") { out <- df break } } out 没有对等的标准。事实上,map(和类似的函数)在可读性方面优于一般循环的一个原因是它们具有绝对可预测的行

假设我想运行一个循环,直到满足一个条件,此时结果被保存,循环退出:

library(tidyverse)

for (i in 1:5) {

  df <- iris %>% select(i) %>% head(2)

  if (names(df) == "Petal.Width") {
    out <- df
    break 

  }
}

out

没有对等的标准。事实上,
map
(和类似的函数)在可读性方面优于一般循环的一个原因是它们具有绝对可预测的行为:它们将为每个元素执行一次函数,没有例外(除非,呃,如果有例外:您可以通过
stop
来提出一个条件,以短路执行,但这是非常不可取的)

相反,您的案例不需要
map
,它需要类似
purr::keep
purr::reduce
的内容

可以这样想:
map
reduce
等都是抽象,对应于更一般的
for
循环的特定特殊情况。它们的目的是明确处理的是哪种特殊情况。作为程序员,您的任务就是找到正确的抽象

在您的特定情况下,我可能会使用dplyr完全重写语句,因此给出“最佳”purrr解决方案很难:最佳解决方案是不使用purrr。也就是说,您可以使用
purrr::detect
,如下所示:

names(iris) %>%
    detect(`==`, 'Sepal.Width') %>%
    `[`(iris, .) %>%
    head(2)

…但实际上,这里是dplyr供比较:

iris %>%
    select(Sepal.Width) %>%
    head(2)
1)
callCC
可用于获得此效果:

callCC(function(k) {
  fun2 <- function(x) {
    print(x) # just to show that x = 5 is never run
    df <- iris %>% select(x) %>% head(2)
    if (names(df) == "Petal.Width") k(df)
  }
  map_df(1:5, fun2)
})
1a)如果必须使用
fun
而不做任何更改,请尝试以下方法:

callCC(function(k) map_df(1:5, ~ if (!is.null(df <- fun(.x))) k(df)))

这不如(1)和(1a)好,因为它仍然需要迭代1:5的每个元素,但只会调用1:4的
fun
。相反,(1)和(1a)在4上运行
fun
fun2
后实际返回。

如果您需要
休息
,您将无法使用
map
map
函数经过优化,以返回与输入长度相同的向量。它们不是循环的替代品,只是循环最常见用途的方便函数。可能有一种黑客方法可以使用
purr::可能
purr::安全
,在函数内部调用
stop
,但正如@KonradRudolph在下面所说,这可能不是一个好主意,也不是那些helper函数的目的。我目前正在编写一段代码,它完全符合我的建议:它从
lappy
内部发出条件信号。我认为这在我的情况下是合适的,但我不是很确定。你能帮我用
keep
reduce
重写吗?@Shinobi_Atobe请看修改后的答案。谢谢-这实际上来自一个网页抓取练习,我在每页上抓取多个表格,并检查是否满足条件。它太复杂了,所以我把它简化为我问题的本质
callCC(function(k) {
  fun2 <- function(x) {
    print(x) # just to show that x = 5 is never run
    df <- iris %>% select(x) %>% head(2)
    if (names(df) == "Petal.Width") k(df)
  }
  map_df(1:5, fun2)
})
[1] 1
[1] 2
[1] 3
[1] 4
  Petal.Width
1         0.2
2         0.2
callCC(function(k) map_df(1:5, ~ if (!is.null(df <- fun(.x))) k(df)))
f <- function(x, y) if (is.null(x)) fun(y) else x
reduce(1:5, f, .init = NULL)