"James Kanze" a écrit > "Pierre Maurette"
<mmaauurreettttttee.ppiieerrrree@@ffrreeee.ffrr> writes:|> "Christophe" a écrit
|> [...]
|> > J'imagine que c'est le compilateur qui, voyant que la fonction
|> > est récursive, va totalement modifier le code de manière à
|> > renvoyer non pas un nombre, mais une somme de nombre. En fait
|> > c'est le compilateur qui va faire un gros travail dès qu'il
|> > reconnait une fonction récursive. Je me trompe ?
|> Oui, je le crains.
Je ne crois pas. La plupart des compilateurs C++ que je connais
traitent un appel récursif exactement comme n'importe autre appel de
fonction.
C'est exactement ce que j'ai écrit : Christophe demande s'il se trompe
en pensant que le compilateur "voyant que la fonction est
récursive...etc.". Je lui réponds que oui, je pense qu'il se trompe
("crains" est un euphémisme).
S'il se trompe, c'est donc que le compilateur ne fait pas ce qu'il
pense. Et je précise que :
"La récursivité n'existe pas dans le langage. Une fonction est dite
récursive si elle s'appelle elle-même. C'est tout."
Je ne suis pas sûr ce que tu essaies à dire. Le C et le C++
garantissent que les appels récursifs (directs ou indirects)
fonctionnent, dans la mesure qu'on n'épuise pas les ressources
système (pile). Je ne sais pas ce que tu veux de plus -- Lisp (un
langage récursif par excellence) n'en dit pas plus.
Pas vu cette garantie dans les normes C99 et C++98, en tous cas aucune
allusion à la pile sur ce sujet.
Je vais essayer d'expliquer, mais je n'ai jamais fait d'études
d'informatique, donc le vocabulaire est peut-être approximatif. Une
fonction F() est dite récursive si elle contient un appel récursif
direct ou indirect. Un appel récursif est un appel de fonction F1()
qui conduit la fonction F() à être appelée avant que F1() ne retourne.
F1() peut être F(), c'est alors de la récursivité directe.
En cherchant sur le mot lui-même, il est simplement dit que l'appel
récursif est autorisé pour l'ensemble des fonctions, sauf main() pour
laquelle il est justement refusé à la compilation.
[norme C++]
5.2.2 Function call
...
9 Recursive calls are permitted, except to the function named main ...:
[norme C]
6.5.2.2 Function calls
Constraints
...
11 Recursive function calls shall be permitted, both directly and
indirectly through any chain of other functions.
Les normes considèrent que le lecteur sait ce qu'est un appel récursif
et quelles sont les conditions minimales pour qu'il puisse être
fonctionnel (les autres conditions sont dans l'algo et le codage).
Ces conditions sont la réentrance des fonctions. Qu'est-ce ?
Chaque appel d'une fonction provoque la création d'un jeu tout neuf de
variables locales (ou automatiques, etc.). Parmi ces variables
locales, les paramètres passés à la fonction, ou des variables locales
initialisées par ces paramètres, question de vocabulaire.
Imaginons que le déroulement de la fonction soit interrompu par une
interruption (signal) ou un appel de fonction (pouvant être la
fonction elle-même).
Le programme est réentrant si au retour de cet évènement, les
variables locales sont inchangées et la fonction peut donc continuer
son travail, et ceci même si la fonction a été appelée "pendant ce
temps". Ceci couvre le cas où la fonction s'est simplement appelée
elle-même (et ainsi de suite parfois). La même fonction appelée par
plusieurs threads doit également être réentrante. Les normes
garantissent effectivement cette réentrance, mais sans faire de
référence très explicite à la récursivité.
Les fonctions des bibliothèques standard ne sont pas (ou ne sont pas
toutes, ou ne sont pas garanties) réentrantes.
[norme C++]
1.9 Program execution
(voir paragraphes 9 & 10)
[norme C]
5.2.3 Signals and interrupts
1 Functions shall be implemented such that they may be interrupted at
any time by a signal, or may be called by a signal handler, or both,
with no alteration to earlier, but still active, invocations' control
flow (after the interruption), function return values, or objects with
automatic storage duration. All such objects shall be maintained
outside the function image (the instructions that compose the
executable representation of a function) on a per-invocation basis.
Les variables statiques ne sont bien entendu pas concernées par cette
réentrance, mais elles peuvent être utilisées pour ce qu'elles sont.
Une variable de ce type est par essence volatile. Enfin, j'en sais
trop rien, faudrait revoir la définition exacte de la volatilité ...
La présence d'une variable statique dans le code d'une fonction
récursive est parfois le signe d'une mauvaise compréhension, d'un
refus du concept de récursivité de la part du programmeur.
Quid de la pile ? Pour moi, il y a deux types de pile. Tout d'abord,
le concept d'appels emboités, la pile d'appel (la commande stack du
debugger SoftIce donne un exemple) :
main()
f1()
f2()
f3()
quand le code est dans f4().
Pour ce qui est de l'éventuelle pile physique du microprocesseur, la
représentation POURRAIT être :
bloc main()
bloc f1()
bloc f2()
bloc f3()
bloc f4()
Un bloc regroupe variables locales (dont paramètres), adresse de
retour, sauvegarde de certains registres comme EBP par exemple.
En revanche, l'utilisation de la récursion grauitement n'est pas un
idiome classique de C++, et le langage offre de nombreux autres
constructions comme les boucles.
Certainement. le calcul d'une factorielle par récursion est calamiteux
en termes de performances.
Et pourtant ...
J'ai une solution récursive que je trouve indispensable qui me vient à
l'esprit, c'est le parcours de l'ensemble des fichiers d'un disque. Ça
vient certaineement du fait que sous Windows, quand on parcourt un
répertoire, on ne sait pas si on va ramener un fichier ou un
répertoire. Idem pour beaucoup de structures arborescentes.
Une solution comme celle proposée ne serait pas considérée une bonne
solution C++,
Certes, mais ce que fait Christophe est certainement une façon de
faire un grand pas dans la compréhension du fonctionnement des ..
fonctions.
"James Kanze" <kanze@alex.gabi-soft.fr> a écrit > "Pierre Maurette"
<mmaauurreettttttee.ppiieerrrree@@ffrreeee.ffrr> writes:
|> "Christophe" <chrschneider@free.fr> a écrit
|> [...]
|> > J'imagine que c'est le compilateur qui, voyant que la fonction
|> > est récursive, va totalement modifier le code de manière à
|> > renvoyer non pas un nombre, mais une somme de nombre. En fait
|> > c'est le compilateur qui va faire un gros travail dès qu'il
|> > reconnait une fonction récursive. Je me trompe ?
|> Oui, je le crains.
Je ne crois pas. La plupart des compilateurs C++ que je connais
traitent un appel récursif exactement comme n'importe autre appel de
fonction.
C'est exactement ce que j'ai écrit : Christophe demande s'il se trompe
en pensant que le compilateur "voyant que la fonction est
récursive...etc.". Je lui réponds que oui, je pense qu'il se trompe
("crains" est un euphémisme).
S'il se trompe, c'est donc que le compilateur ne fait pas ce qu'il
pense. Et je précise que :
"La récursivité n'existe pas dans le langage. Une fonction est dite
récursive si elle s'appelle elle-même. C'est tout."
Je ne suis pas sûr ce que tu essaies à dire. Le C et le C++
garantissent que les appels récursifs (directs ou indirects)
fonctionnent, dans la mesure qu'on n'épuise pas les ressources
système (pile). Je ne sais pas ce que tu veux de plus -- Lisp (un
langage récursif par excellence) n'en dit pas plus.
Pas vu cette garantie dans les normes C99 et C++98, en tous cas aucune
allusion à la pile sur ce sujet.
Je vais essayer d'expliquer, mais je n'ai jamais fait d'études
d'informatique, donc le vocabulaire est peut-être approximatif. Une
fonction F() est dite récursive si elle contient un appel récursif
direct ou indirect. Un appel récursif est un appel de fonction F1()
qui conduit la fonction F() à être appelée avant que F1() ne retourne.
F1() peut être F(), c'est alors de la récursivité directe.
En cherchant sur le mot lui-même, il est simplement dit que l'appel
récursif est autorisé pour l'ensemble des fonctions, sauf main() pour
laquelle il est justement refusé à la compilation.
[norme C++]
5.2.2 Function call
...
9 Recursive calls are permitted, except to the function named main ...:
[norme C]
6.5.2.2 Function calls
Constraints
...
11 Recursive function calls shall be permitted, both directly and
indirectly through any chain of other functions.
Les normes considèrent que le lecteur sait ce qu'est un appel récursif
et quelles sont les conditions minimales pour qu'il puisse être
fonctionnel (les autres conditions sont dans l'algo et le codage).
Ces conditions sont la réentrance des fonctions. Qu'est-ce ?
Chaque appel d'une fonction provoque la création d'un jeu tout neuf de
variables locales (ou automatiques, etc.). Parmi ces variables
locales, les paramètres passés à la fonction, ou des variables locales
initialisées par ces paramètres, question de vocabulaire.
Imaginons que le déroulement de la fonction soit interrompu par une
interruption (signal) ou un appel de fonction (pouvant être la
fonction elle-même).
Le programme est réentrant si au retour de cet évènement, les
variables locales sont inchangées et la fonction peut donc continuer
son travail, et ceci même si la fonction a été appelée "pendant ce
temps". Ceci couvre le cas où la fonction s'est simplement appelée
elle-même (et ainsi de suite parfois). La même fonction appelée par
plusieurs threads doit également être réentrante. Les normes
garantissent effectivement cette réentrance, mais sans faire de
référence très explicite à la récursivité.
Les fonctions des bibliothèques standard ne sont pas (ou ne sont pas
toutes, ou ne sont pas garanties) réentrantes.
[norme C++]
1.9 Program execution
(voir paragraphes 9 & 10)
[norme C]
5.2.3 Signals and interrupts
1 Functions shall be implemented such that they may be interrupted at
any time by a signal, or may be called by a signal handler, or both,
with no alteration to earlier, but still active, invocations' control
flow (after the interruption), function return values, or objects with
automatic storage duration. All such objects shall be maintained
outside the function image (the instructions that compose the
executable representation of a function) on a per-invocation basis.
Les variables statiques ne sont bien entendu pas concernées par cette
réentrance, mais elles peuvent être utilisées pour ce qu'elles sont.
Une variable de ce type est par essence volatile. Enfin, j'en sais
trop rien, faudrait revoir la définition exacte de la volatilité ...
La présence d'une variable statique dans le code d'une fonction
récursive est parfois le signe d'une mauvaise compréhension, d'un
refus du concept de récursivité de la part du programmeur.
Quid de la pile ? Pour moi, il y a deux types de pile. Tout d'abord,
le concept d'appels emboités, la pile d'appel (la commande stack du
debugger SoftIce donne un exemple) :
main()
f1()
f2()
f3()
quand le code est dans f4().
Pour ce qui est de l'éventuelle pile physique du microprocesseur, la
représentation POURRAIT être :
bloc main()
bloc f1()
bloc f2()
bloc f3()
bloc f4()
Un bloc regroupe variables locales (dont paramètres), adresse de
retour, sauvegarde de certains registres comme EBP par exemple.
En revanche, l'utilisation de la récursion grauitement n'est pas un
idiome classique de C++, et le langage offre de nombreux autres
constructions comme les boucles.
Certainement. le calcul d'une factorielle par récursion est calamiteux
en termes de performances.
Et pourtant ...
J'ai une solution récursive que je trouve indispensable qui me vient à
l'esprit, c'est le parcours de l'ensemble des fichiers d'un disque. Ça
vient certaineement du fait que sous Windows, quand on parcourt un
répertoire, on ne sait pas si on va ramener un fichier ou un
répertoire. Idem pour beaucoup de structures arborescentes.
Une solution comme celle proposée ne serait pas considérée une bonne
solution C++,
Certes, mais ce que fait Christophe est certainement une façon de
faire un grand pas dans la compréhension du fonctionnement des ..
fonctions.
"James Kanze" a écrit > "Pierre Maurette"
<mmaauurreettttttee.ppiieerrrree@@ffrreeee.ffrr> writes:|> "Christophe" a écrit
|> [...]
|> > J'imagine que c'est le compilateur qui, voyant que la fonction
|> > est récursive, va totalement modifier le code de manière à
|> > renvoyer non pas un nombre, mais une somme de nombre. En fait
|> > c'est le compilateur qui va faire un gros travail dès qu'il
|> > reconnait une fonction récursive. Je me trompe ?
|> Oui, je le crains.
Je ne crois pas. La plupart des compilateurs C++ que je connais
traitent un appel récursif exactement comme n'importe autre appel de
fonction.
C'est exactement ce que j'ai écrit : Christophe demande s'il se trompe
en pensant que le compilateur "voyant que la fonction est
récursive...etc.". Je lui réponds que oui, je pense qu'il se trompe
("crains" est un euphémisme).
S'il se trompe, c'est donc que le compilateur ne fait pas ce qu'il
pense. Et je précise que :
"La récursivité n'existe pas dans le langage. Une fonction est dite
récursive si elle s'appelle elle-même. C'est tout."
Je ne suis pas sûr ce que tu essaies à dire. Le C et le C++
garantissent que les appels récursifs (directs ou indirects)
fonctionnent, dans la mesure qu'on n'épuise pas les ressources
système (pile). Je ne sais pas ce que tu veux de plus -- Lisp (un
langage récursif par excellence) n'en dit pas plus.
Pas vu cette garantie dans les normes C99 et C++98, en tous cas aucune
allusion à la pile sur ce sujet.
Je vais essayer d'expliquer, mais je n'ai jamais fait d'études
d'informatique, donc le vocabulaire est peut-être approximatif. Une
fonction F() est dite récursive si elle contient un appel récursif
direct ou indirect. Un appel récursif est un appel de fonction F1()
qui conduit la fonction F() à être appelée avant que F1() ne retourne.
F1() peut être F(), c'est alors de la récursivité directe.
En cherchant sur le mot lui-même, il est simplement dit que l'appel
récursif est autorisé pour l'ensemble des fonctions, sauf main() pour
laquelle il est justement refusé à la compilation.
[norme C++]
5.2.2 Function call
...
9 Recursive calls are permitted, except to the function named main ...:
[norme C]
6.5.2.2 Function calls
Constraints
...
11 Recursive function calls shall be permitted, both directly and
indirectly through any chain of other functions.
Les normes considèrent que le lecteur sait ce qu'est un appel récursif
et quelles sont les conditions minimales pour qu'il puisse être
fonctionnel (les autres conditions sont dans l'algo et le codage).
Ces conditions sont la réentrance des fonctions. Qu'est-ce ?
Chaque appel d'une fonction provoque la création d'un jeu tout neuf de
variables locales (ou automatiques, etc.). Parmi ces variables
locales, les paramètres passés à la fonction, ou des variables locales
initialisées par ces paramètres, question de vocabulaire.
Imaginons que le déroulement de la fonction soit interrompu par une
interruption (signal) ou un appel de fonction (pouvant être la
fonction elle-même).
Le programme est réentrant si au retour de cet évènement, les
variables locales sont inchangées et la fonction peut donc continuer
son travail, et ceci même si la fonction a été appelée "pendant ce
temps". Ceci couvre le cas où la fonction s'est simplement appelée
elle-même (et ainsi de suite parfois). La même fonction appelée par
plusieurs threads doit également être réentrante. Les normes
garantissent effectivement cette réentrance, mais sans faire de
référence très explicite à la récursivité.
Les fonctions des bibliothèques standard ne sont pas (ou ne sont pas
toutes, ou ne sont pas garanties) réentrantes.
[norme C++]
1.9 Program execution
(voir paragraphes 9 & 10)
[norme C]
5.2.3 Signals and interrupts
1 Functions shall be implemented such that they may be interrupted at
any time by a signal, or may be called by a signal handler, or both,
with no alteration to earlier, but still active, invocations' control
flow (after the interruption), function return values, or objects with
automatic storage duration. All such objects shall be maintained
outside the function image (the instructions that compose the
executable representation of a function) on a per-invocation basis.
Les variables statiques ne sont bien entendu pas concernées par cette
réentrance, mais elles peuvent être utilisées pour ce qu'elles sont.
Une variable de ce type est par essence volatile. Enfin, j'en sais
trop rien, faudrait revoir la définition exacte de la volatilité ...
La présence d'une variable statique dans le code d'une fonction
récursive est parfois le signe d'une mauvaise compréhension, d'un
refus du concept de récursivité de la part du programmeur.
Quid de la pile ? Pour moi, il y a deux types de pile. Tout d'abord,
le concept d'appels emboités, la pile d'appel (la commande stack du
debugger SoftIce donne un exemple) :
main()
f1()
f2()
f3()
quand le code est dans f4().
Pour ce qui est de l'éventuelle pile physique du microprocesseur, la
représentation POURRAIT être :
bloc main()
bloc f1()
bloc f2()
bloc f3()
bloc f4()
Un bloc regroupe variables locales (dont paramètres), adresse de
retour, sauvegarde de certains registres comme EBP par exemple.
En revanche, l'utilisation de la récursion grauitement n'est pas un
idiome classique de C++, et le langage offre de nombreux autres
constructions comme les boucles.
Certainement. le calcul d'une factorielle par récursion est calamiteux
en termes de performances.
Et pourtant ...
J'ai une solution récursive que je trouve indispensable qui me vient à
l'esprit, c'est le parcours de l'ensemble des fichiers d'un disque. Ça
vient certaineement du fait que sous Windows, quand on parcourt un
répertoire, on ne sait pas si on va ramener un fichier ou un
répertoire. Idem pour beaucoup de structures arborescentes.
Une solution comme celle proposée ne serait pas considérée une bonne
solution C++,
Certes, mais ce que fait Christophe est certainement une façon de
faire un grand pas dans la compréhension du fonctionnement des ..
fonctions.
Les normes précisent les effets, non les moyens. En C++, §5.2.2/9 :
« Recursive calls are permitted, except to the function named main. »
Nous citons le même passage, et pour cause : c'est la seule allusion nette à
Techniquement, les paramètres sont créés par la fonction appelante, à la
site de l'appel. Ils sont donc des variables temporaires de la fonction
appelante, et non de la fonction appelée. Mais ça ne change rien du
principe. De même qu'en C++, formellement, les variables locales sont
créées à l'entrée d'un bloc, et non d'une fonction. Mais là aussi, je
chipote.
Je chipote également. Je vois les paramètres réels comme des rvalues de
En fait, techniquement, on comprend bien les raisons. Les écritures ne
sont pas toujours atomiques, et c'est tout à fait possible de générer
une représentation illégale s'il n'y a qu'une partie de l'écriture qui
s'est faite.
Voir ci-dessus -- tu peux écrire (mais non lire) une variable de type
sig_atomic_t volatile dans le signal. C'est tout.
Je ne connais ces notions de volatilité et d'accès concurrent que par
Comme tu as rémarqué, la norme ne parle pas de pile. En revanche, c'est
impossible à implémenter la recursion sans pile -- il faut donc que
l'implémentation ait une pile quelque part. Mais cette pile peut prendre
de nombreuses formes : sur la plupart des processeurs modernes, il y a
une seule pile, contigüe, gérée par le hardware, qui contient les
adresses de retour et les données locales.
Le mot pile est ambiguë (pour une fois, au même titre que l'anglais stack,
Mais je crois qu'il a bien
existé des architectures à deux piles, une pour les adresses de retour,
et une pour les données locales.
Il faudrait voir des trucs comme les PIC de Microchip, qui sont en
en Lisp, je m'attends à ce que le compilateur génère une boucle. En C++,
je ne m'y attends pas.
Connais pas Lisp. Mon petit frère qui est prof de Math Spé et que
Les normes précisent les effets, non les moyens. En C++, §5.2.2/9 :
« Recursive calls are permitted, except to the function named main. »
Nous citons le même passage, et pour cause : c'est la seule allusion nette à
Techniquement, les paramètres sont créés par la fonction appelante, à la
site de l'appel. Ils sont donc des variables temporaires de la fonction
appelante, et non de la fonction appelée. Mais ça ne change rien du
principe. De même qu'en C++, formellement, les variables locales sont
créées à l'entrée d'un bloc, et non d'une fonction. Mais là aussi, je
chipote.
Je chipote également. Je vois les paramètres réels comme des rvalues de
En fait, techniquement, on comprend bien les raisons. Les écritures ne
sont pas toujours atomiques, et c'est tout à fait possible de générer
une représentation illégale s'il n'y a qu'une partie de l'écriture qui
s'est faite.
Voir ci-dessus -- tu peux écrire (mais non lire) une variable de type
sig_atomic_t volatile dans le signal. C'est tout.
Je ne connais ces notions de volatilité et d'accès concurrent que par
Comme tu as rémarqué, la norme ne parle pas de pile. En revanche, c'est
impossible à implémenter la recursion sans pile -- il faut donc que
l'implémentation ait une pile quelque part. Mais cette pile peut prendre
de nombreuses formes : sur la plupart des processeurs modernes, il y a
une seule pile, contigüe, gérée par le hardware, qui contient les
adresses de retour et les données locales.
Le mot pile est ambiguë (pour une fois, au même titre que l'anglais stack,
Mais je crois qu'il a bien
existé des architectures à deux piles, une pour les adresses de retour,
et une pour les données locales.
Il faudrait voir des trucs comme les PIC de Microchip, qui sont en
en Lisp, je m'attends à ce que le compilateur génère une boucle. En C++,
je ne m'y attends pas.
Connais pas Lisp. Mon petit frère qui est prof de Math Spé et que
Les normes précisent les effets, non les moyens. En C++, §5.2.2/9 :
« Recursive calls are permitted, except to the function named main. »
Nous citons le même passage, et pour cause : c'est la seule allusion nette à
Techniquement, les paramètres sont créés par la fonction appelante, à la
site de l'appel. Ils sont donc des variables temporaires de la fonction
appelante, et non de la fonction appelée. Mais ça ne change rien du
principe. De même qu'en C++, formellement, les variables locales sont
créées à l'entrée d'un bloc, et non d'une fonction. Mais là aussi, je
chipote.
Je chipote également. Je vois les paramètres réels comme des rvalues de
En fait, techniquement, on comprend bien les raisons. Les écritures ne
sont pas toujours atomiques, et c'est tout à fait possible de générer
une représentation illégale s'il n'y a qu'une partie de l'écriture qui
s'est faite.
Voir ci-dessus -- tu peux écrire (mais non lire) une variable de type
sig_atomic_t volatile dans le signal. C'est tout.
Je ne connais ces notions de volatilité et d'accès concurrent que par
Comme tu as rémarqué, la norme ne parle pas de pile. En revanche, c'est
impossible à implémenter la recursion sans pile -- il faut donc que
l'implémentation ait une pile quelque part. Mais cette pile peut prendre
de nombreuses formes : sur la plupart des processeurs modernes, il y a
une seule pile, contigüe, gérée par le hardware, qui contient les
adresses de retour et les données locales.
Le mot pile est ambiguë (pour une fois, au même titre que l'anglais stack,
Mais je crois qu'il a bien
existé des architectures à deux piles, une pour les adresses de retour,
et une pour les données locales.
Il faudrait voir des trucs comme les PIC de Microchip, qui sont en
en Lisp, je m'attends à ce que le compilateur génère une boucle. En C++,
je ne m'y attends pas.
Connais pas Lisp. Mon petit frère qui est prof de Math Spé et que
a écrit
[...]
Nous sommes d'accord sur l'essentiel me semble-t-il. C'est un peu
comme pour les chiens et les chats. From scratch, c'est "très
pareil", un toutou et un minet : même nombre de pattes, une queue,
deux yeux avec des trous dans la peau par chance situés juste en face
des yeux, même constructeur de copie (plus bruyant pour Grosminet que
pour Droopy, mais bon...) , etc. Et pourtant, quand on parle
chien-chat, c'est toujours sur les différences que l'on focalise ;-)Les normes précisent les effets, non les moyens. En C++, §5.2.2/9 :
« Recursive calls are permitted, except to the function named
main. »
Nous citons le même passage, et pour cause : c'est la seule allusion
nette à l'appel récusif d'une fonction. Je l'interprète peut-être un
peu différemment que vous, pour moi c'est une simple possibilité
syntaxique, comme l'appel da main() est simplement refusé à la
compilation. J'ai l'impression que la norme me dit : "L'appel
récursif, je vous le permet. A vous de voir si ça va fonctionner
conformément à vos attentes". Et c'est ailleurs que l'on va trouver
des éléments nous permettant de savoir que, bien géré, ça va
fonctgionner. "permitted" n'est pas "supported" ni même
"allowed". MAis effectivement, je sodomise le diptère.
Techniquement, les paramètres sont créés par la fonction appelante,
à la site de l'appel. Ils sont donc des variables temporaires de la
fonction appelante, et non de la fonction appelée. Mais ça ne change
rien du principe. De même qu'en C++, formellement, les variables
locales sont créées à l'entrée d'un bloc, et non d'une
fonction. Mais là aussi, je chipote.
Je chipote également. Je vois les paramètres réels comme des rvalues
de l'appelant qui initialisent "juste avant l'appel" des variables
locales de la fonction. Pendant la durée de vie d'un appel de la
fonction, il n'y a aucune différence entre ses variables locales et
ses paramètres.
Techniquement, le compilateur est PARFOIS très économe : il empile un
paramètre, et la fonction, sans transfert supplémentaire, le "connait"
sous un "nom" ressemblant à type ptr [ESP - n] ou type ptr [EBP -
n]. J'admet que c'est imagé, lié à un processeur, et pas obligatoire
du tout, il existe de plus plusieurs conventions d'appel.
En fait, techniquement, on comprend bien les raisons. Les écritures
ne sont pas toujours atomiques, et c'est tout à fait possible de
générer une représentation illégale s'il n'y a qu'une partie de
l'écriture qui s'est faite.
Voir ci-dessus -- tu peux écrire (mais non lire) une variable de
type sig_atomic_t volatile dans le signal. C'est tout.
Je ne connais ces notions de volatilité et d'accès concurrent que par
l'assembleur, et à l'opposée par les ressources de mon OS. Je dois
dire que l'aspect C/C++ de la chose m'effraye un peu.
Comme tu as rémarqué, la norme ne parle pas de pile. En revanche,
c'est impossible à implémenter la recursion sans pile -- il faut
donc que l'implémentation ait une pile quelque part. Mais cette pile
peut prendre de nombreuses formes : sur la plupart des processeurs
modernes, il y a une seule pile, contigüe, gérée par le hardware,
qui contient les adresses de retour et les données locales.
Le mot pile est ambiguë (pour une fois, au même titre que l'anglais
stack, qui n'est guère plus précis). Au moins trois niveaux :
* Une notion purement intellectuelle : la pile d'appel.
* Un objet algorithmique, comme dans la STL.
* La pile (pas nécesssairement unique) du processeur.
Le point commun, c'est LIFO. Entendre parler de "pile FIFO" me
hérisse la pilosité sur laquelle je suis assis.
Le mot "pipe(line)" (et pourquoi pas tuyau ?) rentre heureusement dans
les us.Mais je crois qu'il a bien existé des architectures à deux piles,
une pour les adresses de retour, et une pour les données locales.
Il faudrait voir des trucs comme les PIC de Microchip, qui sont en
architecture Harvard.
en Lisp, je m'attends à ce que le compilateur génère une boucle. En
C++, je ne m'y attends pas.
Connais pas Lisp. Mon petit frère qui est prof de Math Spé et que
l'informatique fatigue (tout comme les maths!) me parle d'un truc
nommé je crois Maple, qui gère la récusivité "en tant que telle", en
optimisant.
<kanze@gabi-soft.fr> a écrit
[...]
Nous sommes d'accord sur l'essentiel me semble-t-il. C'est un peu
comme pour les chiens et les chats. From scratch, c'est "très
pareil", un toutou et un minet : même nombre de pattes, une queue,
deux yeux avec des trous dans la peau par chance situés juste en face
des yeux, même constructeur de copie (plus bruyant pour Grosminet que
pour Droopy, mais bon...) , etc. Et pourtant, quand on parle
chien-chat, c'est toujours sur les différences que l'on focalise ;-)
Les normes précisent les effets, non les moyens. En C++, §5.2.2/9 :
« Recursive calls are permitted, except to the function named
main. »
Nous citons le même passage, et pour cause : c'est la seule allusion
nette à l'appel récusif d'une fonction. Je l'interprète peut-être un
peu différemment que vous, pour moi c'est une simple possibilité
syntaxique, comme l'appel da main() est simplement refusé à la
compilation. J'ai l'impression que la norme me dit : "L'appel
récursif, je vous le permet. A vous de voir si ça va fonctionner
conformément à vos attentes". Et c'est ailleurs que l'on va trouver
des éléments nous permettant de savoir que, bien géré, ça va
fonctgionner. "permitted" n'est pas "supported" ni même
"allowed". MAis effectivement, je sodomise le diptère.
Techniquement, les paramètres sont créés par la fonction appelante,
à la site de l'appel. Ils sont donc des variables temporaires de la
fonction appelante, et non de la fonction appelée. Mais ça ne change
rien du principe. De même qu'en C++, formellement, les variables
locales sont créées à l'entrée d'un bloc, et non d'une
fonction. Mais là aussi, je chipote.
Je chipote également. Je vois les paramètres réels comme des rvalues
de l'appelant qui initialisent "juste avant l'appel" des variables
locales de la fonction. Pendant la durée de vie d'un appel de la
fonction, il n'y a aucune différence entre ses variables locales et
ses paramètres.
Techniquement, le compilateur est PARFOIS très économe : il empile un
paramètre, et la fonction, sans transfert supplémentaire, le "connait"
sous un "nom" ressemblant à type ptr [ESP - n] ou type ptr [EBP -
n]. J'admet que c'est imagé, lié à un processeur, et pas obligatoire
du tout, il existe de plus plusieurs conventions d'appel.
En fait, techniquement, on comprend bien les raisons. Les écritures
ne sont pas toujours atomiques, et c'est tout à fait possible de
générer une représentation illégale s'il n'y a qu'une partie de
l'écriture qui s'est faite.
Voir ci-dessus -- tu peux écrire (mais non lire) une variable de
type sig_atomic_t volatile dans le signal. C'est tout.
Je ne connais ces notions de volatilité et d'accès concurrent que par
l'assembleur, et à l'opposée par les ressources de mon OS. Je dois
dire que l'aspect C/C++ de la chose m'effraye un peu.
Comme tu as rémarqué, la norme ne parle pas de pile. En revanche,
c'est impossible à implémenter la recursion sans pile -- il faut
donc que l'implémentation ait une pile quelque part. Mais cette pile
peut prendre de nombreuses formes : sur la plupart des processeurs
modernes, il y a une seule pile, contigüe, gérée par le hardware,
qui contient les adresses de retour et les données locales.
Le mot pile est ambiguë (pour une fois, au même titre que l'anglais
stack, qui n'est guère plus précis). Au moins trois niveaux :
* Une notion purement intellectuelle : la pile d'appel.
* Un objet algorithmique, comme dans la STL.
* La pile (pas nécesssairement unique) du processeur.
Le point commun, c'est LIFO. Entendre parler de "pile FIFO" me
hérisse la pilosité sur laquelle je suis assis.
Le mot "pipe(line)" (et pourquoi pas tuyau ?) rentre heureusement dans
les us.
Mais je crois qu'il a bien existé des architectures à deux piles,
une pour les adresses de retour, et une pour les données locales.
Il faudrait voir des trucs comme les PIC de Microchip, qui sont en
architecture Harvard.
en Lisp, je m'attends à ce que le compilateur génère une boucle. En
C++, je ne m'y attends pas.
Connais pas Lisp. Mon petit frère qui est prof de Math Spé et que
l'informatique fatigue (tout comme les maths!) me parle d'un truc
nommé je crois Maple, qui gère la récusivité "en tant que telle", en
optimisant.
a écrit
[...]
Nous sommes d'accord sur l'essentiel me semble-t-il. C'est un peu
comme pour les chiens et les chats. From scratch, c'est "très
pareil", un toutou et un minet : même nombre de pattes, une queue,
deux yeux avec des trous dans la peau par chance situés juste en face
des yeux, même constructeur de copie (plus bruyant pour Grosminet que
pour Droopy, mais bon...) , etc. Et pourtant, quand on parle
chien-chat, c'est toujours sur les différences que l'on focalise ;-)Les normes précisent les effets, non les moyens. En C++, §5.2.2/9 :
« Recursive calls are permitted, except to the function named
main. »
Nous citons le même passage, et pour cause : c'est la seule allusion
nette à l'appel récusif d'une fonction. Je l'interprète peut-être un
peu différemment que vous, pour moi c'est une simple possibilité
syntaxique, comme l'appel da main() est simplement refusé à la
compilation. J'ai l'impression que la norme me dit : "L'appel
récursif, je vous le permet. A vous de voir si ça va fonctionner
conformément à vos attentes". Et c'est ailleurs que l'on va trouver
des éléments nous permettant de savoir que, bien géré, ça va
fonctgionner. "permitted" n'est pas "supported" ni même
"allowed". MAis effectivement, je sodomise le diptère.
Techniquement, les paramètres sont créés par la fonction appelante,
à la site de l'appel. Ils sont donc des variables temporaires de la
fonction appelante, et non de la fonction appelée. Mais ça ne change
rien du principe. De même qu'en C++, formellement, les variables
locales sont créées à l'entrée d'un bloc, et non d'une
fonction. Mais là aussi, je chipote.
Je chipote également. Je vois les paramètres réels comme des rvalues
de l'appelant qui initialisent "juste avant l'appel" des variables
locales de la fonction. Pendant la durée de vie d'un appel de la
fonction, il n'y a aucune différence entre ses variables locales et
ses paramètres.
Techniquement, le compilateur est PARFOIS très économe : il empile un
paramètre, et la fonction, sans transfert supplémentaire, le "connait"
sous un "nom" ressemblant à type ptr [ESP - n] ou type ptr [EBP -
n]. J'admet que c'est imagé, lié à un processeur, et pas obligatoire
du tout, il existe de plus plusieurs conventions d'appel.
En fait, techniquement, on comprend bien les raisons. Les écritures
ne sont pas toujours atomiques, et c'est tout à fait possible de
générer une représentation illégale s'il n'y a qu'une partie de
l'écriture qui s'est faite.
Voir ci-dessus -- tu peux écrire (mais non lire) une variable de
type sig_atomic_t volatile dans le signal. C'est tout.
Je ne connais ces notions de volatilité et d'accès concurrent que par
l'assembleur, et à l'opposée par les ressources de mon OS. Je dois
dire que l'aspect C/C++ de la chose m'effraye un peu.
Comme tu as rémarqué, la norme ne parle pas de pile. En revanche,
c'est impossible à implémenter la recursion sans pile -- il faut
donc que l'implémentation ait une pile quelque part. Mais cette pile
peut prendre de nombreuses formes : sur la plupart des processeurs
modernes, il y a une seule pile, contigüe, gérée par le hardware,
qui contient les adresses de retour et les données locales.
Le mot pile est ambiguë (pour une fois, au même titre que l'anglais
stack, qui n'est guère plus précis). Au moins trois niveaux :
* Une notion purement intellectuelle : la pile d'appel.
* Un objet algorithmique, comme dans la STL.
* La pile (pas nécesssairement unique) du processeur.
Le point commun, c'est LIFO. Entendre parler de "pile FIFO" me
hérisse la pilosité sur laquelle je suis assis.
Le mot "pipe(line)" (et pourquoi pas tuyau ?) rentre heureusement dans
les us.Mais je crois qu'il a bien existé des architectures à deux piles,
une pour les adresses de retour, et une pour les données locales.
Il faudrait voir des trucs comme les PIC de Microchip, qui sont en
architecture Harvard.
en Lisp, je m'attends à ce que le compilateur génère une boucle. En
C++, je ne m'y attends pas.
Connais pas Lisp. Mon petit frère qui est prof de Math Spé et que
l'informatique fatigue (tout comme les maths!) me parle d'un truc
nommé je crois Maple, qui gère la récusivité "en tant que telle", en
optimisant.
D'après ce que j'ai entendu dire, les premières implémentations de
Forth utilisait deux piles, une pour les données, et une pour les
adresses de rétour des souprogrammes. Étant donné sa structure, la
même chose ne m étonnerait pas des interpréteurs de PostScript
aujourd'hui.
D'après ce que j'ai entendu dire, les premières implémentations de
Forth utilisait deux piles, une pour les données, et une pour les
adresses de rétour des souprogrammes. Étant donné sa structure, la
même chose ne m étonnerait pas des interpréteurs de PostScript
aujourd'hui.
D'après ce que j'ai entendu dire, les premières implémentations de
Forth utilisait deux piles, une pour les données, et une pour les
adresses de rétour des souprogrammes. Étant donné sa structure, la
même chose ne m étonnerait pas des interpréteurs de PostScript
aujourd'hui.
Pourquoi pas simplement : une fonction F() est dite récursive si elle
contient un appel à elle-même, que cet appel soit direct ou indirect.
Pourquoi pas simplement : une fonction F() est dite récursive si elle
contient un appel à elle-même, que cet appel soit direct ou indirect.
Pourquoi pas simplement : une fonction F() est dite récursive si elle
contient un appel à elle-même, que cet appel soit direct ou indirect.
Loïc Joly writes:
| wrote:
|
| <Mode chipotage>
| > Pourquoi pas simplement : une fonction F() est dite récursive si elle
| > contient un appel à elle-même, que cet appel soit direct ou indirect.
|
| La fonction suivante est-elle récursive ?
|
| void f()
| {
| if (false)
| {
| f();
| }
| }
Oui.
-- Gaby
Loïc Joly <loic.actarus.joly@wanadoo.fr> writes:
| kanze@gabi-soft.fr wrote:
|
| <Mode chipotage>
| > Pourquoi pas simplement : une fonction F() est dite récursive si elle
| > contient un appel à elle-même, que cet appel soit direct ou indirect.
|
| La fonction suivante est-elle récursive ?
|
| void f()
| {
| if (false)
| {
| f();
| }
| }
Oui.
-- Gaby
Loïc Joly writes:
| wrote:
|
| <Mode chipotage>
| > Pourquoi pas simplement : une fonction F() est dite récursive si elle
| > contient un appel à elle-même, que cet appel soit direct ou indirect.
|
| La fonction suivante est-elle récursive ?
|
| void f()
| {
| if (false)
| {
| f();
| }
| }
Oui.
-- Gaby