Chamar explicitamente retorno em uma função ou não


199

Há um tempo , fui repreendido por Simon Urbanek, da equipe principal do R (acredito), por recomendar que um usuário chamasse explicitamente returnno final de uma função (seu comentário foi excluído):

foo = function() {
  return(value)
}

em vez disso, ele recomendou:

foo = function() {
  value
}

Provavelmente em uma situação como esta é necessário:

foo = function() {
 if(a) {
   return(a)
 } else {
   return(b)
 }
}

Seu comentário esclareceu por que não ligar, a returnmenos que estritamente necessário seja uma coisa boa, mas isso foi excluído.

Minha pergunta é: por que não ligar returnmais rápido ou melhor e, portanto, preferível?


12
returné desnecessário mesmo no último exemplo. A remoção returnpode torná-lo um pouco mais rápido, mas, na minha opinião, isso ocorre porque R é considerado uma linguagem de programação funcional.
31412 kohske

4
@kohske Você poderia expandir seu comentário em uma resposta, incluindo mais detalhes sobre por que é mais rápido e como isso está relacionado ao R ser uma linguagem de programação funcional?
Paul Hiemstra

2
returninduz salto não local e o salto não local explícito é incomum para FP. Na verdade, por exemplo, o esquema não possui return. Eu acho que meus comentários são muito curtos (e talvez incorretos) como resposta.
31412 kohske

2
Se F # não tem return, break, continuetambém, que às vezes é tedioso.
colinfang

Respostas:


129

A pergunta era: Por que a chamada (explicitamente) não é o retorno mais rápido ou melhor e, portanto, preferível?

Não há nenhuma declaração na documentação do R que faça essa suposição.
A página principal? 'Function' diz:

function( arglist ) expr
return(value)

É mais rápido sem ligar para retornar?

Ambas function()e return()são funções primitivas e o function()próprio retorna o último valor avaliado, mesmo sem incluir a return()função.

Chamar return()como .Primitive('return')com esse último valor como argumento fará o mesmo trabalho, mas precisa de uma chamada a mais. Para que essa .Primitive('return')chamada (geralmente) desnecessária possa atrair recursos adicionais. A medição simples, no entanto, mostra que a diferença resultante é muito pequena e, portanto, não pode ser a razão para não usar retorno explícito. O seguinte gráfico é criado a partir dos dados selecionados desta maneira:

bench_nor2 <- function(x,repeats) { system.time(rep(
# without explicit return
(function(x) vector(length=x,mode="numeric"))(x)
,repeats)) }

bench_ret2 <- function(x,repeats) { system.time(rep(
# with explicit return
(function(x) return(vector(length=x,mode="numeric")))(x)
,repeats)) }

maxlen <- 1000
reps <- 10000
along <- seq(from=1,to=maxlen,by=5)
ret <- sapply(along,FUN=bench_ret2,repeats=reps)
nor <- sapply(along,FUN=bench_nor2,repeats=reps)
res <- data.frame(N=along,ELAPSED_RET=ret["elapsed",],ELAPSED_NOR=nor["elapsed",])

# res object is then visualized
# R version 2.15

Comparação do tempo decorrido da função

A imagem acima pode ser um pouco diferente na sua plataforma. Com base nos dados medidos, o tamanho do objeto retornado não está causando nenhuma diferença, o número de repetições (mesmo se aumentado) faz apenas uma diferença muito pequena, que em palavras reais com dados reais e algoritmo real não poderia ser contada ou fazer com que seu script executado mais rápido.

É melhor sem ligar de volta?

Return é uma boa ferramenta para projetar claramente "folhas" de código onde a rotina deve terminar, sair da função e retornar valor.

# here without calling .Primitive('return')
> (function() {10;20;30;40})()
[1] 40
# here with .Primitive('return')
> (function() {10;20;30;40;return(40)})()
[1] 40
# here return terminates flow
> (function() {10;20;return();30;40})()
NULL
> (function() {10;20;return(25);30;40})()
[1] 25
> 

Depende da estratégia e do estilo de programação do programador, que estilo ele usa, ele não pode usar nenhum retorno (), pois não é necessário.

Os programadores do núcleo R usam ambas as abordagens, ie. com e sem return explícito (), como é possível encontrar em fontes de funções 'base'.

Muitas vezes, apenas return () é usado (sem argumento) retornando NULL nos casos para interromper condicionalmente a função.

Não está claro se é melhor ou não, pois o usuário ou analista padrão usando R não pode ver a diferença real.

Minha opinião é que a pergunta deve ser: Existe algum perigo em usar retorno explícito vindo da implementação do R?

Ou, talvez melhor, o usuário que escreve o código da função sempre deve perguntar: Qual é o efeito de não usar retorno explícito (ou colocar o objeto a ser retornado como a última folha da ramificação do código) no código da função?


4
Obrigado por uma resposta muito boa. Eu acredito que não há perigo em usar return, e tudo se resume à preferência do programador em usá-lo ou não.
Paul Hiemstra

38
A velocidade de returné realmente a última coisa com a qual você deve se preocupar.
hadley

2
Eu acho que essa é uma resposta ruim. As razões se resumem a uma discordância fundamental do valor do uso de returnchamadas de função desnecessárias . A pergunta que você deve fazer não é a que você propõe no final. Em vez disso, é: “por que devo usar um redundante return? Que benefício isso proporciona? ” Como se vê, a resposta é "não muito", ou mesmo "nenhuma". Sua resposta falha em apreciar isso.
Konrad Rudolph

@KonradRudolph ... você repetiu o que Paulo estava perguntando originalmente (por que o retorno explícito é ruim). E eu gostaria de saber a resposta certa (a certa para qualquer um) também :). Considera fornecer sua explicação para os usuários deste site?
Petr Matousu 30/03

1
@Dason Em outro lugar, vinculei um post do Reddit explicando por que esse argumento é falho nesse contexto. Infelizmente, o comentário parece ser removido automaticamente todas as vezes. A versão curta returné como um comentário explícito que diz "incrementar x em 1", ao lado de um pedaço de código em execução x = x + 2. Em outras palavras, sua explicitação é (a) totalmente irrelevante e (b) transmite a informação errada . Porque returna semântica em R é, simplesmente, "abortar esta função". Ele não significa o mesmo que returnem outras línguas.
Konrad Rudolph

103

Se todo mundo concorda que

  1. return não é necessário no final do corpo de uma função
  2. não usar returné marginalmente mais rápido (de acordo com o teste de Alan, 4,3 microssegundos versus 5,1)

todos devemos parar de usar returnno final de uma função? Certamente não vou, e gostaria de explicar o porquê. Espero ouvir se outras pessoas compartilham minha opinião. E peço desculpas se não for uma resposta direta ao OP, mas mais como um longo comentário subjetivo.

Meu principal problema em não usar returné que, como Paul apontou, há outros lugares no corpo de uma função onde você pode precisar. E se você é forçado a usar returnem algum lugar no meio da sua função, por que não tornar todas as returninstruções explícitas? Eu odeio ser inconsistente. Também acho que o código lê melhor; pode-se digitalizar a função e ver facilmente todos os pontos e valores de saída.

Paulo usou este exemplo:

foo = function() {
 if(a) {
   return(a)
 } else {
   return(b)
 }
}

Infelizmente, pode-se salientar que pode ser facilmente reescrito como:

foo = function() {
 if(a) {
   output <- a
 } else {
   output <- b
 }
output
}

A última versão está em conformidade com alguns padrões de programação que defendem uma declaração de retorno por função. Eu acho que um exemplo melhor poderia ter sido:

bar <- function() {
   while (a) {
      do_stuff
      for (b) {
         do_stuff
         if (c) return(1)
         for (d) {
            do_stuff
            if (e) return(2)
         }
      }
   }
   return(3)
}

Seria muito mais difícil reescrever usando uma única declaração de retorno: seria necessário vários se breakum sistema intrincado de variáveis ​​booleanas para propagá-las. Tudo isso para dizer que a regra de retorno único não funciona bem com R. Então, se você precisar usar returnem alguns locais do corpo da sua função, por que não ser consistente e usá-la em qualquer lugar?

Não acho que o argumento da velocidade seja válido. Uma diferença de 0,8 microssegundo não é nada quando você começa a observar as funções que realmente fazem alguma coisa. A última coisa que vejo é que é menos digitada, mas ei, não sou preguiçosa.


7
+1, há uma clara necessidade da returndeclaração em alguns casos, como o @flodel mostrou. Como alternativa, há situações em que uma declaração de retorno é melhor omitida, por exemplo, muitas e pequenas chamadas de função. Em todos os outros, digamos 95%, dos casos, não importa realmente se alguém usa returnou não, e tudo se resume a preferência. Eu gosto de usar return, pois é mais explícito no que você quer dizer, portanto, mais legível. Talvez essa discussão seja semelhante a <-vs =?
Paul Hiemstra

7
Isso está tratando R como uma linguagem de programação imperativa, o que não é: é uma linguagem de programação funcional. A programação funcional simplesmente funciona de maneira diferente, e usar returnpara retornar um valor não faz sentido, a par da escrita, em if (x == TRUE)vez de if (x).
Konrad Rudolph

4
Você também reescreve foocomo foo <- function(x) if (a) a else b(com quebras de linha, conforme necessário). Não há necessidade de retorno explícito ou valor intermediário.
Hadley

26

Esta é uma discussão interessante. Eu acho que o exemplo do @ flodel é excelente. No entanto, acho que ilustra meu argumento (e o @koshke menciona isso em um comentário) que returnfaz sentido quando você usa um imperativo em vez de um estilo de codificação funcional .

Não para enfatizar a questão, mas eu teria reescrito fooassim:

foo = function() ifelse(a,a,b)

Um estilo funcional evita alterações de estado, como armazenar o valor de output. Nesse estilo, returnestá fora de lugar; fooparece mais uma função matemática.

Concordo com o @flodel: usar um sistema complexo de variáveis ​​booleanas barseria menos claro e inútil quando você tiver return. O que torna bartão acessível às returndeclarações é que elas são escritas em um estilo imperativo. De fato, as variáveis ​​booleanas representam as alterações de "estado" evitadas em um estilo funcional.

É realmente difícil reescrever barno estilo funcional, porque é apenas pseudocódigo, mas a ideia é algo como isto:

e_func <- function() do_stuff
d_func <- function() ifelse(any(sapply(seq(d),e_func)),2,3)
b_func <- function() {
  do_stuff
  ifelse(c,1,sapply(seq(b),d_func))
}

bar <- function () {
   do_stuff
   sapply(seq(a),b_func) # Not exactly correct, but illustrates the idea.
}

O whileloop seria o mais difícil de reescrever, porque é controlado por alterações de estado em a.

A perda de velocidade causada por uma chamada para returné insignificante, mas a eficiência obtida ao evitar returne reescrever em um estilo funcional é muitas vezes enorme. Dizer aos novos usuários que parem de usar returnprovavelmente não ajudará, mas guiá-los para um estilo funcional resultará em recompensa.


@Paul returné necessário em estilo imperativo, porque muitas vezes você deseja sair da função em diferentes pontos de um loop. Um estilo funcional não usa loops e, portanto, não precisa return. Em um estilo puramente funcional, a chamada final é quase sempre o valor de retorno desejado.

No Python, as funções requerem uma returndeclaração. No entanto, se você programou sua função em um estilo funcional, provavelmente terá apenas uma returninstrução: no final de sua função.

Usando um exemplo de outra postagem StackOverflow, digamos que queremos uma função que retorne TRUEse todos os valores em um determinado xtiverem um comprimento ímpar. Nós poderíamos usar dois estilos:

# Procedural / Imperative
allOdd = function(x) {
  for (i in x) if (length(i) %% 2 == 0) return (FALSE)
  return (TRUE)
}

# Functional
allOdd = function(x) 
  all(length(x) %% 2 == 1)

Em um estilo funcional, o valor a ser retornado naturalmente cai no final da função. Mais uma vez, parece mais uma função matemática.

@ GSee Os avisos descritos ?ifelsesão definitivamente interessantes, mas não acho que eles estejam tentando dissuadir o uso da função. De fato, ifelsetem a vantagem de vetorizar funções automaticamente. Por exemplo, considere uma versão ligeiramente modificada de foo:

foo = function(a) { # Note that it now has an argument
 if(a) {
   return(a)
 } else {
   return(b)
 }
}

Esta função funciona bem quando length(a)é 1. Mas se você reescrever foocom umifelse

foo = function (a) ifelse(a,a,b)

Agora foofunciona em qualquer comprimento de a. De fato, funcionaria mesmo quando aé uma matriz. Retornar um valor da mesma forma que testum recurso que ajuda na vetorização, não é um problema.


Não está claro para mim por returnque não se encaixa com um estilo funcional de programação. Se um está programando imperativamente ou funcionalmente, em algum estágio uma função ou sub-rotina precisa retornar algo. Por exemplo, a programação funcional em python ainda requer uma returndeclaração. Você poderia elaborar mais sobre este ponto.
Paul Hiemstra

Nessa situação, usar ifelse(a,a,b)é uma irritação minha. Parece que todas as frases ?ifelseestão gritando: "não me use em vez de if (a) {a} else b". ex. "... retorna um valor com a mesma forma que test", "se yesou nofor muito curto, seus elementos forem reciclados.", "o modo do resultado pode depender do valor de test", "o atributo de classe do resultado é retirado teste pode ser inapropriado para os valores selecionados de yese no"
GSee

Na segunda olhada, foonão faz muito sentido; sempre retornará TRUE ou b. A utilização ifelseretornará 1 ou mais TRUEs e / ou 1 ou vários bs. Inicialmente, pensei que a intenção da função era dizer "se alguma afirmação é VERDADEIRA, retorne alguma coisa, caso contrário, retorne outra coisa". Eu não acho que isso deva ser vetorizado, porque então seria "retornar os elementos de algum objeto que é VERDADEIRO e, para todos os elementos que não são VERDADEIROS, retornar b."
GSee

22

Parece que sem return()ele é mais rápido ...

library(rbenchmark)
x <- 1
foo <- function(value) {
  return(value)
}
fuu <- function(value) {
  value
}
benchmark(foo(x),fuu(x),replications=1e7)
    test replications elapsed relative user.self sys.self user.child sys.child
1 foo(x)     10000000   51.36 1.185322     51.11     0.11          0         0
2 fuu(x)     10000000   43.33 1.000000     42.97     0.05          0         0

____ EDITAR__ _ __ _ __ _ __ _ __ _ ___

Eu prossigo para outros benchmark ( benchmark(fuu(x),foo(x),replications=1e7)) e o resultado é revertido ... vou tentar em um servidor.


Você poderia comentar o motivo pelo qual essa diferença ocorre?
Paul Hiemstra

4
A resposta de @PaulHiemstra Petr cobre uma das principais razões para isso; duas chamadas ao usar return(), uma se não o fizer. É totalmente redundante no final de uma função, pois function()retorna seu último valor. Você só perceberá isso em muitas repetições de uma função em que pouco é feito internamente, de forma que o custo return()se torne grande parte do tempo total de cálculo da função.
Gavin Simpson

13

Um problema em não colocar 'return' explicitamente no final é que, se adicionarmos instruções adicionais ao final do método, subitamente o valor de retorno estará errado:

foo <- function() {
    dosomething()
}

Isso retorna o valor de dosomething().

Agora chegamos ao dia seguinte e adicionamos uma nova linha:

foo <- function() {
    dosomething()
    dosomething2()
}

Queríamos que nosso código retornasse o valor de dosomething(), mas, em vez disso, não retorna mais.

Com um retorno explícito, isso se torna realmente óbvio:

foo <- function() {
    return( dosomething() )
    dosomething2()
}

Podemos ver que há algo estranho nesse código e corrigi-lo:

foo <- function() {
    dosomething2()
    return( dosomething() )
}

1
Sim, na verdade, acho que um retorno explícito () é útil ao depurar; uma vez que o código é limpo, a sua necessidade é menos convincente e eu prefiro a elegância de não tê-la ...
PatrickT

Mas isso não é realmente um problema no código real, é puramente teórico. O código que pode sofrer com isso tem um problema muito maior: um fluxo de código obscuro e quebradiço que é tão óbvio que simples adições o quebram.
Konrad Rudolph

@KonradRudolph Eu acho que você está meio que fazendo um escocês não verdadeiro ;-) "Se isso é um problema no seu código, você é um péssimo programador!". Eu realmente não concordo. Eu acho que, embora você possa se dar bem com atalhos em pequenos pedaços de código, onde você conhece todas as linhas de cor, isso voltará a mordê-lo à medida que seu código aumentar.
Hugh Perkins

2
@HughPerkins Não é um escocês de verdade ; é uma observação empírica sobre a complexidade do código, apoiada por décadas de boas práticas de engenharia de software: mantenha as funções individuais curtas e o fluxo de código óbvio. E omitir returnnão é um atalho, é o estilo adequado na programação funcional. O uso de returnchamadas de função desnecessárias é uma instância da programação do culto à carga .
Konrad Rudolph

Bem ... não vejo como isso impede que você adicione algo após a sua returndeclaração e não perceba que ela não será executada. Você poderia muito bem adicionar um comentário após o valor que você deseja retornar por exemplodosomething() # this is my return value, don't add anything after it unless you know goddam well what you are doing
lebatsnok

10

Minha pergunta é: Por que não ligar returnmais rápido

É mais rápido porque returné uma função (primitiva) em R, o que significa que usá-la no código incorre no custo de uma chamada de função. Compare isso com a maioria das outras linguagens de programação, onde returnhá uma palavra-chave, mas não uma chamada de função: ela não se traduz em nenhuma execução de código de tempo de execução.

Dito isto, chamar uma função primitiva dessa maneira é muito rápido em R, e chamar returngera uma sobrecarga minúscula. Este não é o argumento para omitir return.

ou melhor e, portanto, preferível?

Porque não há razão para usá-lo.

Porque é redundante e não adiciona redundância útil .

Para ser claro: a redundância às vezes pode ser útil . Mas a maioria das redundâncias não é desse tipo. Em vez disso, é do tipo que adiciona desordem visual sem adicionar informações: é o equivalente de programação de uma palavra de preenchimento ou de um gráfico ).

Considere o exemplo a seguir de um comentário explicativo, que é universalmente reconhecido como redundância ruim porque o comentário apenas parafraseia o que o código já expressa:

# Add one to the result
result = x + 1

O uso returnem R se enquadra na mesma categoria, porque R é uma linguagem de programação funcional e em R toda chamada de função tem um valor . Essa é uma propriedade fundamental de R. E quando você vê o código R da perspectiva de que toda expressão (incluindo toda chamada de função) tem um valor, a pergunta se torna: "por que devo usar return?" É preciso haver um motivo positivo, pois o padrão é não usá-lo.

Um desses motivos positivos é sinalizar a saída antecipada de uma função, digamos em uma cláusula de guarda :

f = function (a, b) {
    if (! precondition(a)) return() # same as `return(NULL)`!
    calculation(b)
}

Este é um uso válido e não redundante de return. No entanto, essas cláusulas de guarda são raras em R, em comparação com outros idiomas, e como toda expressão tem um valor, um regular ifnão exige return:

sign = function (num) {
    if (num > 0) {
        1
    } else if (num < 0) {
        -1
    } else {
        0
    }
}

Podemos até reescrever fassim:

f = function (a, b) {
    if (precondition(a)) calculation(b)
}

... onde if (cond) expré o mesmo que if (cond) expr else NULL.

Por fim, gostaria de evitar três objeções comuns:

  1. Algumas pessoas argumentam que o uso returnacrescenta clareza, porque sinaliza "essa função retorna um valor". Mas, como explicado acima, toda função retorna algo em R. Pensar returncomo um marcador de retornar um valor não é apenas redundante, é ativamente enganador .

  2. De maneira semelhante, o Zen do Python tem uma orientação maravilhosa que sempre deve ser seguida:

    Explícito é melhor que implícito.

    Como o descarte redundante returnnão viola isso? Como o valor de retorno de uma função em uma linguagem funcional é sempre explícito: é sua última expressão. Esse é novamente o mesmo argumento sobre explicitação versus redundância.

    De fato, se você quiser uma explicação explícita, use-a para destacar a exceção à regra: marque funções que não retornam um valor significativo, que são chamadas apenas pelos efeitos colaterais (como cat). Excepto R tem um marcador melhor do que returnpara este caso: invisible. Por exemplo, eu escreveria

    save_results = function (results, file) {
        # … code that writes the results to a file …
        invisible()
    }
  3. Mas e as funções longas? Não será fácil perder a noção do que está sendo devolvido?

    Duas respostas: primeiro, não realmente. A regra é clara: a última expressão de uma função é seu valor. Não há nada para acompanhar.

    Mais importante, porém, o problema em funções longas não é a falta de returnmarcadores explícitos . É o comprimento da função . As funções longas quase (?) Sempre violam o princípio da responsabilidade única e, mesmo quando não o fazem, serão beneficiadas por serem separadas por legibilidade.


Talvez eu deva acrescentar que algumas pessoas advogam o uso returnpara torná-lo mais semelhante a outros idiomas. Mas esse é um argumento ruim: outras linguagens de programação funcional também não costumam usar return. São apenas linguagens imperativas , onde nem toda expressão tem um valor, que a utilizam.
Konrad Rudolph

Cheguei a esta pergunta com a opinião de que o uso de returnsuportes é explicitamente melhor e leia sua resposta com críticas completas. Sua resposta me levou a refletir sobre essa visão. Eu acho que a necessidade de usar returnexplicitamente (pelo menos no meu próprio caso) está ligada à necessidade de melhor revisar minhas funções posteriormente. Com a noção de que minhas funções simplesmente podem ser muito complexas, agora posso ver que um objetivo para melhorar meu estilo de programação seria esforçar-se por estruturar os códigos para manter a explicitação sem a return. Obrigado por essas reflexões e idéias !!
Kasper Thystrup Karstensen

6

Eu penso returnnisso como um truque. Como regra geral, o valor da última expressão avaliada em uma função se torna o valor da função - e esse padrão geral é encontrado em muitos lugares. Todos os seguintes são avaliados para 3:

local({
1
2
3
})

eval(expression({
1
2
3
}))

(function() {
1
2
3
})()

O returnque realmente não está retornando um valor (isso é feito com ou sem ele), mas "rompendo" a função de maneira irregular. Nesse sentido, é o equivalente mais próximo da instrução GOTO em R (também existem interrupções e próximas). Eu uso returnmuito raramente e nunca no final de uma função.

 if(a) {
   return(a)
 } else {
   return(b)
 }

... isso pode ser reescrito, o if(a) a else bque é muito melhor legível e menos bracketish. Aqui não há necessidade return. Meu caso prototípico de uso de "retorno" seria algo como ...

ugly <- function(species, x, y){
   if(length(species)>1) stop("First argument is too long.")
   if(species=="Mickey Mouse") return("You're kidding!")
   ### do some calculations 
   if(grepl("mouse", species)) {
      ## do some more calculations
      if(species=="Dormouse") return(paste0("You're sleeping until", x+y))
      ## do some more calculations
      return(paste0("You're a mouse and will be eating for ", x^y, " more minutes."))
      }
   ## some more ugly conditions
   # ...
   ### finally
   return("The end")
   }

Geralmente, a necessidade de muitos retornos sugere que o problema é feio ou mal estruturado.

<>

return realmente não precisa de uma função para funcionar: você pode usá-la para interromper um conjunto de expressões a serem avaliadas.

getout <- TRUE 
# if getout==TRUE then the value of EXP, LOC, and FUN will be "OUTTA HERE"
# .... if getout==FALSE then it will be `3` for all these variables    

EXP <- eval(expression({
   1
   2
   if(getout) return("OUTTA HERE")
   3
   }))

LOC <- local({
   1
   2
   if(getout) return("OUTTA HERE")
   3
   })

FUN <- (function(){
   1
   2
   if(getout) return("OUTTA HERE")
   3
   })()

identical(EXP,LOC)
identical(EXP,FUN)

Hoje encontrei um caso em que alguém pode realmente precisar return(meu exemplo feio acima é altamente artificial): suponha que você precise testar se um valor é NULLou NA: nesses casos, retorne uma string vazia, caso contrário, retorne o charactervalor. Mas um teste is.na(NULL)fornece um erro, portanto parece que isso só pode ser feito com if(is.null(x)) return("")e depois continuado if(is.na(x)) ...... (Pode-se usar length(x)==0em vez de is.null(x)mas ainda assim, não é possível usar length(x)==0 | is.na(x)se xé NULL.)
lebatsnok

1
Isso |ocorre porque você usou (OR vetorizado onde ambos os lados são avaliados) em vez de ||(OR curto-circuito, não vetorizado, onde os predicados são avaliados por sua vez). Considere if (TRUE | stop()) print(1)versusif (TRUE || stop()) print(1)
asac 19/08/19

2

return pode aumentar a legibilidade do código:

foo <- function() {
    if (a) return(a)       
    b     
}

3
Talvez isso possa acontecer. Mas isso não acontece no seu exemplo. Em vez disso, obscurece (ou melhor, complexifica) o fluxo de código.
Konrad Rudolph

1
sua função pode ser simplificada para: foo <- function() a || b(o que é IMO mais legível, em qualquer caso, não há legibilidade "puro", mas a legibilidade na opinião de alguém: há pessoas que dizem linguagem assembly é perfeitamente legível)
lebatsnok

1

O argumento da redundância surgiu muito aqui. Na minha opinião, isso não é motivo suficiente para omitir return(). Redundância não é automaticamente uma coisa ruim. Quando usada estrategicamente, a redundância torna o código mais claro e mais sustentável.

Considere este exemplo: Os parâmetros de função geralmente têm valores padrão. Portanto, especificar um valor igual ao padrão é redundante. Exceto que torna óbvio o comportamento que eu espero. Não há necessidade de acessar a página de manual da função para me lembrar quais são os padrões. E não se preocupe com uma versão futura da função alterando seus padrões.

Com uma penalidade de desempenho insignificante por ligar return()(de acordo com os benchmarks publicados por outros), tudo se resume ao estilo, e não ao certo e ao errado. Para que algo esteja "errado", é preciso haver uma clara desvantagem, e ninguém aqui demonstrou satisfatoriamente que incluir ou omitir return()tem uma desvantagem consistente. Parece muito específico do caso e do usuário.

Então aqui é onde eu estou sobre isso.

function(){
  #do stuff
  ...
  abcd
}

Estou desconfortável com variáveis ​​"órfãs", como no exemplo acima. Seria abcdparte de uma declaração que eu não terminei de escrever? É um remanescente de uma emenda / edição no meu código e precisa ser excluído? Colei / movi acidentalmente algo de outro lugar?

function(){
  #do stuff
  ...
  return(abdc)
}

Por outro lado, este segundo exemplo torna óbvio para mim que é um valor de retorno pretendido, em vez de algum acidente ou código incompleto. Para mim, essa redundância não é absolutamente inútil.

Obviamente, quando a função estiver concluída e funcionando, eu poderia remover o retorno. Mas removê-lo é em si um passo extra redundante e, a meu ver, é mais inútil do que incluir return()em primeiro lugar.

Tudo isso dito, eu não uso, return()em suma, funções de uma linha sem nome. Lá, ele compõe uma grande fração do código da função e, portanto, causa principalmente confusão visual que torna o código menos legível. Mas, para funções maiores formalmente definidas e nomeadas, eu a uso e provavelmente continuarei a fazê-lo.


"O abcd faria parte de uma declaração que eu não terminei de escrever?" - Como isso é diferente de qualquer outra expressão que você escreve? Acho que esse é o cerne de nossa discordância. Ter uma posição variável por si só pode ser peculiar em uma linguagem de programação imperativa, mas é completamente normal e esperado em uma linguagem de programação funcional. A questão, afirmo, é simplesmente que você não está familiarizado com programação funcional (o fato de falar sobre "declarações" em vez de "expressões" reforça isso).
Konrad Rudolph

É diferente porque todas as outras afirmações geralmente fazem algo de uma maneira mais óbvia: é uma atribuição, uma comparação, uma chamada de função ... Sim, meus primeiros passos de codificação foram em linguagens imperativas e eu ainda uso linguagens imperativas. Ter dicas visuais uniformes nos idiomas (onde quer que os idiomas permitam) facilita meu trabalho. A return()em R não custa nada. É objetivamente redundante, mas ser "inútil" é seu julgamento subjetivo. Redundante e inútil não são necessariamente sinônimos. É aí que discordamos.
cymon 29/11/19

Além disso, não sou engenheiro de software nem cientista da computação. Não leia muitas nuances sobre o meu uso da terminologia.
cymon 29/11/19

Apenas para esclarecer: “Redundante e inútil não são necessariamente sinônimos. É aí que discordamos. - Não, concordo totalmente com isso e expliquei esse ponto na minha resposta. A redundância pode ser útil ou até crucial . Mas isso precisa ser mostrado ativamente, não assumido. Entendo seu argumento sobre o motivo pelo qual você acha que isso é válido returne, embora não esteja convencido, acho que é potencialmente válido (definitivamente está em uma linguagem imperativa ... minha crença é que não se traduz em linguagens funcionais).
Konrad Rudolph
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.