116 votes

Est une fonctionnalité en elle-même et la récursivité ?

...ou est-ce juste une pratique?

Je vous pose cette question à cause d'une dispute avec mon professeur: "j'ai perdu de crédit pour l'appel d'une fonction récursive sur la base que nous ne traitons pas la récursivité dans la classe, et mon argument est que nous avons appris implicitement, par l'apprentissage, return et les méthodes.

Je pose la question ici parce que je soupçonne que quelqu'un a une réponse définitive.

Par exemple, quelle est la différence entre les deux méthodes suivantes:

public static void a() {
    return a();
    }

public static void b() {
    return a();
    }

Autre que "a continue pour toujours" (dans le programme il est utilisé correctement pour inviter un utilisateur à nouveau lorsqu'il est fourni avec une entrée non valide), il n'existe aucune différence fondamentale entre l' a et b? Pour un non optimisée compilateur, comment sont-ils traités différemment?

En fin de compte il s'agit de déterminer si l'apprentissage de l' return a() de b que nous avons à cet effet également appris à return a() de a. A nous?

112voto

Ben Aaronson Points 3618

Pour répondre à votre question: Non, du point de vue de l'apprentissage d'une langue, la récursivité n'est pas une fonction. Si votre professeur vraiment ancrée vous des marques à l'aide d'une "fonctionnalité" il n'avait pas enseigné, et pourtant, c'était mal.

En lisant entre les lignes, il est possible qu'en utilisant la récursivité, vous avez évité jamais à l'aide d'une fonctionnalité qui était censé être un résultat d'apprentissage pour son cours. Par exemple, peut-être que vous n'utilisez pas d'itération à tous, ou peut-être que vous ne l'utilisiez for boucles au lieu d'utiliser les deux for et while. Il est courant qu'une cession a pour but de tester votre capacité de faire certaines choses, et si vous évitez de les faire, votre professeur tout simplement ne peut pas vous accorder les marques de mettre de côté pour cette fonction. Cependant, si cela était vraiment la cause de votre perte de repères, le professeur doit prendre cela comme une expérience d'apprentissage de son propre si de démontrer certains résultats de l'apprentissage est l'un des critères pour une mission, qui doit être clairement expliqué aux élèves.

Cela dit, je suis d'accord avec la plupart des autres commentaires et les réponses que l'itération est un meilleur choix que la récursion. Il ya un couple de raisons, et tandis que d'autres personnes ont abordé dans une certaine mesure, je ne suis pas sûr qu'ils ont entièrement expliqué la pensée derrière eux.

Les Débordements De Pile

La plus évidente est que vous risquez d'obtenir une erreur de dépassement de pile. De façon réaliste, la méthode que tu as écrit est très peu probable effectivement conduire à un, puisqu'un utilisateur aura à donner mauvaise saisie à plusieurs reprises de déclencher un débordement de pile.

Cependant, une chose à garder à l'esprit est que non seulement la méthode elle-même, mais d'autres méthodes plus ou moins dans l'appel de la chaîne sera sur la pile. De ce fait, en passant engloutir à disposition d'espace de pile est assez impoli chose pour n'importe quelle méthode pour le faire. Personne ne veut avoir à constamment se soucier d'espace libre pile à chaque fois qu'ils écrivent du code, en raison du risque que d'autres code peut avoir utilisé inutilement beaucoup d'elle.

Cette est une partie d'un principe plus général dans la conception de logiciels appelé l'abstraction. Essentiellement, lorsque vous appelez DoThing(), tous vous avez besoin à prendre en compte est que la Chose est faite. Vous ne devriez pas avoir à vous soucier de la mise en œuvre des détails de la façon dont c'est fait. Mais gourmand utilisation de la pile des sauts de ce principe, parce que chaque morceau de code s'inquiéter de la façon dont beaucoup de la pile, il est raisonnable de supposer qu'il a laissé par le code d'ailleurs dans la chaîne d'appel.

La lisibilité

L'autre raison est la lisibilité. L'idéal ce code doit aspirer à être un homme-document lisible, où chaque ligne décrit simplement ce qu'il fait. Ces deux approches:

private int getInput() {
    int input;
    do {
        input = promptForInput();
    } while (!inputIsValid(input))
    return input;
}

rapport

private int getInput() {
    int input = promptForInput();
    if(inputIsValid(input)) {
        return input;
    }
    return getInput();
}

Oui, ces deux travaux, et oui ils sont à la fois assez facile à comprendre. Mais comment les deux approches décrites en anglais? Je pense que ce serait quelque chose comme:

Je vous invite pour l'entrée jusqu'à l'entrée est valide, puis le retourner

rapport

Je vous invite pour l'entrée, puis si l'entrée est valide, je vais le retourner, sinon je vous l'entrée et renvoie le résultat au lieu

Vous pouvez peut-être penser un peu moins maladroit de formulation pour le dernier, mais je pense que vous trouverez toujours que la première va être une description plus précise, sur le plan conceptuel, de ce que vous êtes en train d'essayer de faire. Ce n'est pas dire que la récursivité est toujours moins lisible. Pour les situations où il brille, comme l'arbre transversal, vous pourriez faire le même genre de côté par côté d'analyse entre la récursivité et d'une autre approche et vous auriez presque certainement trouver la récursivité donne le code qui est plus clairement d'auto-description, ligne par ligne.

Dans l'isolement, ces deux sont les petits points. Il est très peu probable que cela aurait jamais vraiment conduire à un débordement de la pile, et le gain en lisibilité est mineur. Mais n'importe quel programme va être une collection de beaucoup de ces petites décisions, de sorte que même si dans l'isolement, ils n'ont pas d'importance, il est important de connaître les principes qui sous-tendent l'obtention de leur droit.

48voto

Harry Johnston Points 11039

Pour répondre à la question littérale, plutôt que de la méta-question: la récursivité est une fonction, dans le sens que tous les compilateurs et/ou de langues nécessairement le permettent. Dans la pratique, il est attendu de tous (ordinaire), les compilateurs modernes - et certainement tous les compilateurs Java! - mais il n'est pas universellement vrai.

Comme artificiel, pourquoi, par exemple, la récursivité peut pas être pris en charge, envisager un compilateur qui stocke l'adresse de retour d'une fonction dans un emplacement statique; ce peut être le cas, par exemple, pour un compilateur pour un microprocesseur qui n'ont pas de pile.

Pour un compilateur, lorsque vous appelez une fonction comme ceci

a();

il est mis en œuvre comme

move the address of label 1 to variable return_from_a
jump to label function_a
label 1

et la définition de la(),

function a()
{
   var1 = 5;
   return;
}

est mis en œuvre comme

label function_a
move 5 to variable var1
jump to the address stored in variable return_from_a

J'espère que le problème lorsque vous essayez d'appeler a() de façon récursive dans un tel compilateur est évident; le compilateur ne sait plus comment rentrer de l'extérieur appeler, parce que l'adresse de retour a été écrasé.

Pour le compilateur, j'ai effectivement utilisé (fin des années 70 ou début des années 80, je crois), avec pas de support pour la récursivité, le problème était un peu plus subtile que ça: l'adresse de retour est stockée sur la pile, tout comme dans les compilateurs modernes, mais les variables locales ne l'étaient pas. (Théoriquement, cela devrait signifier que la récursivité est possible pour les fonctions avec aucune non-variables locales statiques, mais je ne me souviens pas si le compilateur explicitement pris en charge ou pas. Il peut avoir besoin implicite des variables locales pour une raison quelconque.)

En regardant vers l'avant, je peux imaginer spécialisé scénarios lourdement systèmes parallèles, peut-être - où ne pas avoir à fournir une pile pour chaque thread pourrait être avantageux, et où, par conséquent, la récursivité n'est autorisée que si le compilateur peut refactoriser le code dans une boucle. (Évidemment, le primitif compilateurs je discute ci-dessus n'ont pas été capables de tâches complexes comme la refactorisation de code.)

17voto

mike Points 310

L'enseignant veut savoir si vous avez étudié ou pas. Apparemment vous n'avez pas de résoudre le problème de la manière dont il vous a enseigné (de la bonne façon; itération), et donc, considère que vous n'avez pas. Je suis tout à fait pour des solutions créatives, mais dans ce cas je suis d'accord avec votre professeur pour une raison différente:
Si l'utilisateur fournit une entrée non valide trop grand nombre de fois (c'est à dire en gardant entrer pressé), vous aurez une exception de dépassement de pile et de votre solution de crash. En outre, la solution itérative est plus efficace et plus facile à maintenir. Je pense que c'est pour cette raison que votre enseignant vous ai donné.

13voto

gnasher729 Points 5011

La déduction de points, car "nous n'avons pas couvrir la récursivité dans la classe" est terrible. Si vous avez appris comment appeler Une fonction qui appelle la fonction B qui appelle la fonction C qui retourne à B, ce qui revient à l'Un qui renvoie à l'appelant, et l'enseignant n'a pas dit explicitement qu'il faut les différentes fonctions (ce qui serait le cas dans le vieux FORTRAN versions, par exemple), il n'y a pas de raison que A, B et C ne peuvent pas tous être de la même fonction.

D'autre part, il nous faudrait voir le code de décider si, dans votre cas particulier en utilisant la récursivité est vraiment la bonne chose à faire. Il n'y a pas beaucoup de détails, mais il ne prend son mal.

10voto

Yonatan Nir Points 595

Il y a beaucoup de point de vue pour regarder au sujet de la question que vous avez demandé, mais ce que je peux dire, c'est que du point de vue de l'apprentissage d'une langue, la récursivité n'est pas une fonctionnalité sur son propre. Si votre professeur vraiment ancrée vous des marques à l'aide d'une "fonctionnalité" il n'avait pas enseigné, et pourtant, c'était mal, mais comme je l'ai dit, il y a d'autres points de vue à prendre en considération ici qui, en fait, faire le professeur de cours de droit lors de la déduction de points.

De ce que je peux déduire de votre question, à l'aide d'une fonction récursive, à demander de l'entrée en cas de saisie de l'échec n'est pas une bonne pratique car toutes les fonctions récursives' appel est poussé sur la pile. Depuis cette récursivité est entraînée par la saisie de l'utilisateur il est possible d'avoir une fonction récursive infinie et donc StackOverflow.

Il n'y a pas de différence entre ces 2 exemples que vous avez mentionné dans votre question dans le sens de ce qu'ils font (mais diffèrent dans d'autres façons)- Dans les deux cas, une adresse de retour et toutes les méthode de l'info est en cours de chargement de la pile. Dans une récursivité cas, l'adresse de retour est tout simplement la ligne droite après l'appel de méthode (bien sûr ce n'est pas exactement ce que vous voyez dans le code lui-même, mais plutôt dans le code que le compilateur créé). En Java, C, Python, la récursivité est assez cher par rapport à l'itération (en général), car il nécessite l'attribution d'un nouveau cadre de pile. Ne pas mentionner que vous pouvez obtenir une exception de dépassement de pile si l'entrée n'est pas valide de trop nombreuses fois.

Je crois que le professeur déduit des points depuis la récursivité est considérée comme un sujet de son propre et son peu probable que quelqu'un ayant aucune expérience de la programmation pense de la récursivité. (Bien entendu, cela ne signifie pas qu'elles ne sont pas, mais c'est peu probable).

À mon humble avis, je pense que le professeur est en droit d'en déduire que vous les points. Vous pourriez avoir facilement pris la partie validation d'une méthode différente et de l'utiliser comme ceci:

public bool foo() 
{
  validInput = GetInput();
  while(!validInput)
  {
    MessageBox.Show("Wrong Input, please try again!");
    validInput = GetInput();
  }
  return hasWon(x, y, piece);
}

Si ce que vous avez fait peut en effet être résolu de cette façon, alors ce que vous avez fait était une mauvaise pratique et devrait être évitée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X