Dans les périls de Java écoles Joel discute de son expérience à Penn et la difficulté de "défauts de segmentation". Il dit qu'il dit
Les SEGFAULTS sont difficiles jusqu'à ce que vous] "prenez une profonde respiration et essayez vraiment de forcer votre esprit à travailler à deux niveaux d'abstraction différents simultanément."
Compte tenu d'une liste de causes communes pour Segfault, je ne comprends pas comment nous devons travailler à 2 niveaux d'abstraction.
Pour une raison quelconque, Joel considère que ces concepts noyau à une capacité de programmeurs à résumé. Je ne veux pas supposer trop. Donc, ce qui est si difficile à propos des pointeurs/de la récursivité? Des exemples seraient gentils.
J'ai d'abord remarqué que les pointeurs et la récursives étaient difficiles à l'université. J'avais pris quelques cours de première année typiques (l'un était C et Assembleur, l'autre était dans le cadre du système. Les deux cours ont commencé avec des centaines d'élèves, dont beaucoup ont eu des années d'expérience de programmation de niveau secondaire (typiquement basique et pascal, à cette époque). Mais dès que des pointeurs ont été introduits dans le cours C et que la récursivité a été introduite dans le cours du régime, un grand nombre d'étudiants - peut-être même une majorité - étaient complètement battus. C'étaient des enfants qui avaient écrit beaucoup de code avant et n'avaient aucun problème, mais quand ils ont frappé les pointeurs et la récursivité, ils ont également frappé un mur en termes de capacité cognitive.
Mon hypothèse est que les pointeurs et la récursives sont les mêmes en ce sens qu'ils vous ont besoin de conserver deux niveaux d'abstraction dans votre tête en même temps. Il y a quelque chose sur les multiples niveaux d'abstraction nécessitant un type d'aptitude mentale qu'il est très possible certaines personnes n'auront jamais.
Je serais également parfaitement disposé à accepter qu'il soit possible d'enseigner des pointeurs et/ou de la récursivité à quiconque ... Je n'ai aucune preuve d'une manière ou d'une autre. Je sais que de manière empirique, pouvant vraiment comprendre que ces deux concepts constitue un très bon prédicteur de la capacité de programmation générale et que, dans le cours normal de la formation de CS de premier cycle, ces deux concepts se présentent comme certains des principaux obstacles.
Récursion n'est pas simplement "une fonction qui s'appelle lui-même". Vous n'allez pas vraiment apprécier pourquoi la récursion est difficile tant que vous vous retrouvez en train de vous retrouver sur les cadres de pile pour comprendre ce qui s'est mal passé avec votre analyseur de descente récursif. Vous aurez souvent des fonctions mutuellement récursives (fonctionner une fonction d'appels B, qui appelle la fonction C, qui peut appeler la fonction A). Il peut être très difficile de comprendre ce qui a mal tourné lorsque vous êtes des zones d'empilement profondément dans une série de fonctions mutuellement récursives.
Quant aux pointeurs, à nouveau, le concept des pointeurs est assez simple: une variable qui stocke une adresse mémoire. Mais encore une fois, lorsque quelque chose ne va pas avec votre structure de données compliquée de void**
Pointeurs qui pointent vers différents nœuds, vous verrez pourquoi cela peut être délicat car vous avez du mal à comprendre pourquoi l'un de vos indications pointe vers une adresse déchets.
Java prend en charge les pointeurs (qu'ils s'appellent des références) et appuie la récursivité. Donc, à la surface, son argument semble inutile.
Ce qu'il parle vraiment de la capacité de déboguer. A Java pointeur (ERR, référence) est garanti de pointer à un objet valide. Le pointeur AC n'est pas. Et le truc dans la programmation C, supposant que vous n'utilisez pas d'outils comme - Valgrind, est de savoir exactement où vous avez vagé un pointeur (il est rarement situé au point situé dans une standingTrace).
Le problème avec les pointeurs et la récursivité n'est pas que ce ne sont pas nécessairement difficiles à comprendre, mais qu'ils sont mal enseignés, surtout en ce qui concerne des langues telles que c ou c ++ (principalement parce que les langues elles-mêmes enseignent mal ). Chaque fois que j'entends (ou lisez) quelqu'un dit "un tableau est juste un pointeur" Je meurs un peu à l'intérieur.
De même, chaque fois que quelqu'un utilise la fonction Fibonacci pour illustrer la récursion que je veux crier. C'est un mauvais exemple car la version itérative n'est pas plus difficile à écrire et Il fonctionne au moins ou mieux que la tâche récursive, et cela ne vous donne aucune réelle idée de savoir pourquoi une solution récursive serait utile ou souhaitable. Quicksort, traverse des arbres, etc., sont loin meilleurs exemples pour la raison pour laquelle et comment de la récursion.
Avoir à boue avec des pointeurs est un artefact de travailler dans un langage de programmation qui les expose. Les générations de programmeurs de Fortran étaient des listes de construction et des arbres et des piles et des files d'attente sans avoir besoin d'un type de pointeur dédié (ou d'une allocation de mémoire dynamique), et je n'ai jamais entendu personne accuser le foretran d'être une langue de jouets.
Pointeurs et récursion sont deux bêtes séparées et il y a des raisons différentes qui se qualifient chaque comme étant " difficile ".
En général, les pointeurs ont besoin d'un modèle mental différent de l'affectation pure variable. Quand j'ai une variable de pointeur, il est juste que: un pointeur vers un autre objet, les seules données qu'il contient est l'adresse de mémoire qu'il pointe. Ainsi, par exemple, si j'ai un pointeur int32 et lui affecter une valeur directement, je ne change pas la valeur de l'int, je suis pointant vers une nouvelle adresse de mémoire (il y a beaucoup de trucs soignées que vous pouvez faire avec ce ). Encore plus intéressant est d'avoir un pointeur vers un pointeur (ce qui arrive lorsque vous passez une variable Ref en fonction du paramètre en C #, la fonction peut assigner un objet tout à fait différent du paramètre et cette valeur sera toujours portée lorsque la fonction sorties.
Récursion prend une légère saut mental lors de la première apprentissage parce que vous définissez une fonction en termes de lui-même. C'est un concept sauvage quand vous tombez sur, mais une fois que vous avez saisi l'idée, il devient une seconde nature.
Mais revenons au sujet à portée de main. L'argument de Joel est pas des pointeurs ou récursivité en soi, mais plutôt le fait que les étudiants sont retirés plus de la façon dont les ordinateurs fonctionnent vraiment. C'est la science en informatique. Il y a une nette différence entre l'apprentissage au programme et apprendre comment fonctionnent les programmes. Je ne pense pas que ce soit une question tant de " Je l'ai appris de cette façon que tout le monde devrait avoir à apprendre de cette façon ", comme lui faisant valoir que de nombreux programmes CS deviennent des écoles de commerce glorifiés.
Je ne suis pas d'accord avec Joel que le problème est celui de penser à plusieurs niveaux d'abstraction en soi, je pense que c'est plus que les pointeurs et la récursives sont deux bons exemples de problèmes nécessitant une modification du modèle mental des personnes de la manière dont les programmes fonctionnent.
Je pense que les pointeurs sont le cas plus simple à illustrer. Traiter avec des pointeurs nécessite un modèle mental de l'exécution du programme qui explique la manière dont les programmes fonctionnent actuellement avec des adresses de mémoire et des données. Mon expérience a été que souvent les programmeurs méchants n'ont même pas pensé à cela avant d'en apprendre davantage sur les pointeurs. Même s'ils le savent dans un sens abstrait, ils ne l'ont pas adopté dans leur modèle cognitif de la manière dont un programme fonctionne. Lorsque les pointeurs sont introduits, il faut un changement fondamental de la manière dont ils pensent comment le code fonctionne.
La récursion est problématique car il y a deux blocs conceptuels à comprendre. Le premier est au niveau de la machine et beaucoup comme les pointeurs qu'il peut être surmonté en développant une bonne compréhension de la manière dont les programmes sont réellement stockés et exécutés. Je pense que l'autre problème de récursivité est que les gens ont une tendance naturelle à essayer de déconstruire un problème récursif en une non récursive, ce qui mène à la compréhension d'une fonction récursive en tant que gestalt. C'est soit un problème avec des personnes ayant un contexte mathématique insuffisant, soit un modèle mental qui ne lie pas la théorie mathématique au développement de programmes.
La chose est que je ne pense pas que les pointeurs et la récursivité sont les seuls deux domaines problématiques pour les personnes coincées dans un modèle mental insuffisant. Le parallélisme semble être un autre domaine que certaines personnes sont simplement bloquées et ont de la difficulté à adapter leur modèle mental à rendre compte, c'est que cela signifie que souvent les pointeurs et la récursivité sont faciles à tester dans une interview.
Je donne à P. Brian A +1, parce que j'ai l'impression de faire: la récursion est un concept aussi fondamental que celui qui a la moindre difficulté avec cela devrait mieux envisager de chercher un emploi chez Mac Donalds, mais il y a ensuite une récursion:
make a burger:
put a cold burger on the grill
wait
flip
wait
hand the fried burger over to the service personel
unless its end of shift: make a burger
Sûrement, le manque de compréhension a également à voir avec nos écoles. Ici, il faut introduire des nombres naturels tels que PEEANO, DEDEKIND et FREUGE. Nous n'avons donc pas tant de difficultés plus tard.
DATA | CODE
|
pointer | recursion SELF REFERENTIAL
----------+---------------------------------
objects | macro SELF MODIFYING
|
|
Le concept de données et de code auto-référentiels sous-tendent respectivement la définition des pointeurs et de la récursivité. Malheureusement, une exposition généralisée à des langages de programmation impérative a conduit les étudiants en informatique à croire qu'ils doivent comprendre la mise en œuvre via le comportement opérationnel de leurs cours lorsqu'ils doivent faire confiance à ce mystère à l'aspect fonctionnel de la langue. Sommant tous les chiffres jusqu'à une centaine semble une simple question de commencer avec un et l'ajout à la suivante dans la séquence et de le faire en arrière à l'aide de fonctions circulaires auto-référentielles semble pervers et même dangereux pour beaucoup pas habitué à la sécurité de la sécurité de fonctions pures. L'expérience passée des langues impératives les a brûlées et le désir de comprendre tous les états intermédiaires est entraîné par une suspicion que les variables sont sujettes à modifier, même si elles saisissent que le même nom ne se réfère pas à la même chose lorsqu'une fonction est invoquée à nouveau. de l'intérieur lui-même.
Le concept de données de modification automatique et de code sous-tend la définition des objets (i.e. Données intelligentes) et des macros respectivement. Je les mentionne comme ils sont encore plus difficiles à comprendre, surtout lorsqu'une compréhension opérationnelle de l'exécution est attendue d'une combinaison de tous les quatre concepts - par exemple. une macro générant un ensemble d'objets qui implémente un analyseur décent récursif à l'aide d'un arbre de pointeurs. Plutôt que de retracer l'ensemble du fonctionnement de l'état du programme étape par étape de chaque couche d'abstraction à la fois, les programmeurs impératifs doivent apprendre à faire confiance à ce que leurs variables ne sont attribuées qu'une fois dans des fonctions pures et que des invocations répétées de la même fonction pure avec Les mêmes arguments génèrent toujours le même résultat (c'est-à-dire une transparence référentielle), même dans une langue qui soutient également les fonctions impures, comme Java. Courir en cercles après l'exécution d'une activité infructueuse. L'abstraction devrait simplifier.