2010-09-22 1 views

Répondre

47

En ignorant les fonctions imbriquées, il est toujours possible de remplacer les calculs Scala par des retours avec des calculs équivalents sans retour. Ce résultat remonte aux débuts de la «programmation structurée», et s'appelle le structured program theorem, assez habilement.

Avec les fonctions imbriquées, la situation change. Scala vous permet de placer un "retour" enfoui profondément dans une série de fonctions imbriquées. Lorsque le retour est exécuté, le contrôle saute hors de toutes les fonctions imbriquées, dans la méthode contenant le plus proche, à partir de laquelle il retourne (en supposant que la méthode est toujours en cours d'exécution, sinon une exception est levée). Ce type de déroulement de pile peut être fait avec des exceptions, mais ne peut pas être fait par une restructuration mécanique du calcul (comme c'est possible sans fonctions imbriquées).

La raison la plus fréquente pour laquelle vous souhaiteriez revenir d'une fonction imbriquée est de sortir d'un bloc impératif pour la compréhension ou le contrôle des ressources. (Le corps d'un impératif-compréhension se traduit à une fonction imbriquée, même si elle ressemble à une déclaration.)

for(i<- 1 to bezillion; j <- i to bezillion+6){ 
if(expensiveCalculation(i, j)){ 
    return otherExpensiveCalculation(i, j) 
} 

withExpensiveResource(urlForExpensiveResource){ resource => 
// do a bunch of stuff 
if(done) return 
//do a bunch of other stuff 
if(reallyDoneThisTime) return 
//final batch of stuff 
} 
+0

Merci pour la réponse.C'est très instructif. – Jus12

+0

Je pense que c'est une réponse plus formelle. Je suis intéressé par la preuve du résultat que vous mentionnez. Pouvez-vous fournir quelques références? – Jus12

+0

Ajout d'un lien vers wikipedia pour le théorème du programme structuré. –

26

Il est prévu afin de prendre en compte les circonstances dans lesquelles il est difficile ou fastidieux de faire converger tous les chemins de flux de contrôle à l'extrémité lexicale du procédé. Comme il est certainement vrai, comme Dave Griffith dit, que vous pouvez éliminer toute utilisation de return, il peut souvent être plus obfusif de le faire que de simplement couper l'exécution court avec un return ouvert.

Sachez également que return renvoie des méthodes, et non des fonctions (littéraux) qui peuvent être définies dans une méthode.

+0

Je savais que c'était la réponse .. Je ne peux pas penser à des exemples. – Jus12

+8

Heureusement pour moi, vous n'avez pas demandé d'exemples ... –

+0

+1 pour obfuscatory –

2

I Vue return comme utile lors de l'écriture du code de style impératif, ce qui signifie généralement I/O code. Si vous faites du code fonctionnel pur, vous n'avez pas besoin (et ne devriez pas utiliser) return. Mais avec du code fonctionnel, vous aurez peut-être besoin de paresse pour obtenir des performances équivalentes à celles d'un code impératif qui peut "s'échapper tôt" en utilisant return.

3

Voici un exemple

Cette méthode a beaucoup de if-else déclarations pour contrôler le flux, parce qu'il n'y a pas de retour (c'est-ce que je suis venu avec, vous pouvez utiliser votre imagination pour l'agrandir). J'ai pris ce à partir d'un exemple concret de la vie et modifia être un code fictif (en fait, il est plus que cela):

Sans retour:

def process(request: Request[RawBuffer]): Result = { 
     if (condition1) { 
     error() 
     } else { 
     val condition2 = doSomethingElse() 
     if (!condition2) { 
      error() 
     } else { 
      val reply = doAnotherThing() 
      if (reply == null) { 
      Logger.warn("Receipt is null. Send bad request") 
      BadRequest("Coudln't receive receipt") 
      } else { 
      reply.hede = initializeHede() 
      if (reply.hede.isGood) { 
       success() 
      } else { 
       error() 
      } 
      } 
     } 
     } 
    } 

Avec le retour:

def process(request: Request[RawBuffer]): Result = { 
     if (condition1) { 
     return error() 
     } 

     val condition2 = doSomethingElse() 
     if (!condition2) { 
     return error() 
     } 

     val reply = doAnotherThing() 

     if (reply == null) { 
     Logger.warn("Receipt is null. Send bad request") 
     return BadRequest("Coudln't receive receipt") 
     } 

     reply.hede = initializeHede() 
     if (reply.hede.isGood) 
     return success() 

     return error() 
    } 

A mes yeux, le second est plus lisible et même plus gérable que le premier. La profondeur de l'indentation (avec un code bien formaté) est profonde et profonde si vous n'utilisez pas une instruction return. Et je ne l'aime pas :)

+2

Je pense que les programmeurs Scala bien expérimentés (pas moi) peuvent suivre le premier extrait plus clairement. – Jus12

+0

En effet, c'est un peu de «goût», dépend du point de vue des développeurs. par exemple. J'aime l'appartement le plus – yerlilbilgin