Эквивалент `break` в мурлыкании :: карта - PullRequest
0 голосов
/ 12 февраля 2019

Скажем, я хочу запустить цикл, пока не будет выполнено условие, после чего результат сохраняется и цикл завершается:

library(tidyverse)

for (i in 1:5) {

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

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

  }
}

out

Как я могу переписать это, используя purr::map без необходимости оценкикаждый i?

Выполнение следующих действий дает нужный мне результат, но должен оценивать 5 раз, тогда как цикл for только 3 раза:

fun <- function(x) {

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

  if (names(df) == "Petal.Width") {
  return(df)
  }
}

map_df(1:5, fun)

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

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)
})

, дающего:

[1] 1
[1] 2
[1] 3
[1] 4
  Petal.Width
1         0.2
2         0.2

1a) Если важно использовать fun без изменений, попробуйте вместо этого:

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

2) purrr :: redu Альтернативой является использование reduce от purrr (или Reduce из базы R):

f <- function(x, y) if (is.null(x)) fun(y) else x
reduce(1:5, f, .init = NULL)

Это не так хорошо, как (1) и (1a) с точки зрения того, что оно все равно будет включать итерации по каждому элементу 1: 5, но будет вызывать только fun для 1: 4.В отличие от (1) и (1a) фактически возвращаются после выполнения fun или fun2 на 4.

0 голосов
/ 12 февраля 2019

Нет эквивалента.Фактически, одна вещь, которая делает map (и подобные функции) настолько превосходящими общие циклы с точки зрения читабельности, состоит в том, что они имеют абсолютно предсказуемое поведение: они будут выполнять функцию ровно один раз для каждого элемента, без исключений (кроме, э-э,если есть исключение: вы можете поднять условие через stop для выполнения короткого замыкания, но это очень редко желательно).

Вместо этого ваше дело не требует map, это требует чего-то вроде purrr::keep или purrr::reduce.

Думайте об этом так: map, reduce и т. Д. Являются абстракциями, которые соответствуют конкретным частным случаямболее общий цикл for.Их цель - прояснить , какой особый случай обрабатывается.Как программист, ваша задача состоит в том, чтобы найти правильную абстракцию.

В вашем конкретном случае я бы, вероятно, полностью переписал бы оператор, используя dplyr, поэтому трудно найти «лучшее» мурлыканье:Лучшее решение не использовать мурлыкать.Тем не менее, вы можете использовать purrr::detect следующим образом:

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

или

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

... но на самом деле, вот dplyr для сравнения:

iris %>%
    select(Sepal.Width) %>%
    head(2)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...