Prog bash récursif factorielle

prog bash récursif factorielle

Bonjour,
je souhaiterais avoir des suggestions de code pour un programme récursif comptant la factoriel d’une valeur donnée.

J’ai fait le prog en itératif, mais en récursif…

voici ci-joint mon prog en itératif:

#!/bin/bash

declare -i r=1

for ((i=1;i<=$1;i++))
do

((r=r*i))

done

echo "factorielle = " $r
~
Merci d’avance pour vos réponses

fonction factorielle(n){
if n==0
return 1;
else
return(n*factorielle(n-1));}

Je ne connais pas la syntaxe en bash.

moi non plus. Je ne pense pas que bash soit fait pour ce genre de prog sans gymnastique.

[code]factorielle () {
if (( $1 <= 1 ))
then
echo 1
return 0
else
echo $(($1*$(factorielle $(($1-1)))))
fi
}

factorielle $1
[/code]

Pour les petites valeurs, ksh est au minimum 10 fois plus rapide que bash (20 fois pour factorielle de 100).

Tu peux le faire au niveau des processus:

#!/bin/sh if [ $1 -eq 0 ] ; then echo 1 else N=$[$1-1] R=`/tmp/fact $N` F=$[$R*$1] echo $F fi
après tu calcules
$ /tmp/fact 1000000
et tu devrais planter ta machine.

Intéressant, :smt001 tu as des explications du pourquoi?

@ripta > tu parle en mode sh ? ou dans leur langages respectifs ? zsh s’en sort comment ?

Pas d’explication particulière. Ksh est un shell plus rapide, c’est tout. Plus récent et sans doute mieux développé. J’ai souvent fait le test de comparaison et c’est toujours ksh qui se révèle le plus rapide.

Le shebang du script appelle le ksh (#!/usr/bin/ksh). Mais tu peux, bien sûr, le lancer depuis n’importe quel shell (bash, csh …). La syntaxe est quasi la même. Les scripts bash peuvent être utilisés tels quels dans ksh. Mais pas l’inverse car ksh offre de nouvelles fonctionnalités qui n’existent pas dans bash. J’aime assez:

  • les indices des tableaux peuvent être associatifs
  • possibilité de limiter la portée des variables (utile pour les fonctions)
  • typage des variables plus fort (déclaration)
  • arithmétique floating point

J’aime moins:

  • le mode d’édition de la ligne de commande (soit vi, soit emacs) moins “naturel” qui le mode console de bash. Je fais mes scripts en ksh mais j’utilise le bash pour les commandes simples en en console.

Et plein d’autres avantages. C’est le shell le plus populaire dans le monde Unix.
unix.com/unix-advanced-exper … shell.html

LE livre de référence: le fameux LTKS (Learning The Korn Shell - O’Reilly)

Voir aussi cette comparaison des shell:
en.wikipedia.org/wiki/Comparison … ter_shells

Le shebang du script appelle le ksh (#!/usr/bin/ksh). Mais tu peux, bien sûr, le lancer depuis n’importe quel shell (bash, csh …). La syntaxe est quasi la même. Les scripts bash peuvent être utilisés tels quels dans ksh. Mais pas l’inverse car ksh offre de nouvelles fonctionnalités qui n’existent pas dans bash.[/quote]
Ok donc tu parle de leur mode natif respectif, mais dans 80% des cas les scripts sont en shell posix. Est ce que là il y a une différence ?

Tu comparait des performances

Le shell est l’interface utilisateur qui permet de communiquer simplement avec le noyau. Je ne suis pas un spécialiste des standards mais il me semble que tous les shell les plus usuels sont tous POSIX.

Tu trouveras sur Wikipedia que le standard POSIX est basé sur le shell Korn (ksh). Assez curieusement dans mon LTKS les auteurs précisent qu’il est plutôt basé sur le Bourne (bash). Vas savoir. Toujours est-il que la norme POSIX n’est pas restrictive dans le sens ou un shell peut implémenter des fonctionnalités non prévues. Ça ne veut pas dire que le shell perd sa conformité. C’est le cas du Korn qui a sensiblement amélioré la sémantique des fonctions et a introduit la portée locale des variables et des “trap” locaux qui n’existent pas en Bourne où tant les variables de fonctions que les “trap” sont partagés par le script parent. Il reste conforme malgré ces améliorations mais il perd en portablilité.

Plus complet, plus rapide plus de fonctionnalités. Achète le LTKS et essaye-le! :wink:

Je cogite sur un exemple pour comparer la sémantique des fonctions et le poste dès que c’est prêt.

[code]#!/bin/sh

script shell.sh

ma_fonction () {
VAR="Variable Interne"
echo 'depuis la fonction $VAR --> ’ $VAR
}

ma_fonction "Appel de fonction"
echo 'hors de la fonction $VAR --> ’ $VAR[/code]
Retourne:
$ ./shell.sh
depuis la fonction $VAR --> Variable Interne
hors de la fonction $VAR --> Variable Interne

[code]#!/usr/bin/ksh

script shell.ksh

function ma_fonction {
typeset VAR="Variable Interne"
echo 'depuis la fonction $VAR --> ’ $VAR
}

ma_fonction "Appel de fonction"
echo 'hors de la fonction $VAR --> ’ $VAR
[/code]
Retourne:
$ ./shell.ksh
depuis la fonction $VAR --> Variable Interne
hors de la fonction $VAR -->

Plus fort encore, quand tu “sources” le script depuis un shell, observe ce qui se passe avec une variable définie avant le “source”:

$ VAR=“Coucou”; . shell.sh; echo $VAR;
$ VAR=“Coucou”; . shell.ksh; echo $VAR;

Étonnant non? Je trouve la gestion de la portée des variables ksh, plus facile à gérer, plus propre. Qui ne s’est jamais arraché les cheveux en se demandant pourquoi cette p… de variable changeait de valeur?

[quote=“ripat”]Le shell est l’interface utilisateur qui permet de communiquer simplement avec le noyau.
(…)[/quote] Pas tout à fait. Le shell communique avec l’OS, qui communique éventuellement avec le noyau.
Sous unix, un shell a plusieurs sens: c’est le plus courament un programme qui permet d’exécuter des commandes. En ce sens, les window managers sont des shells visuels. Mais ceci ce sont des sens dérivés. Dans son sens d’origine, quand on s’intéresse à la gestion des processus dans l’OS, un shell est surtout la “coquille” (=shell) dans laquelle tourne un processus, qui contient le code et l’état de ce processus (BCP), mais aussi l’état de l’environnement dans lequel il tourne, et les handles vers les ressources qu’il réserve (l’état de ses échanges avec le noyau, en fait). Un >exec< par exemple, charge le code du programme appelé, réinitialise le pointeur de programme du BCP au début du nouveau code à exécuter, mais ne change pas l’environnement ni les ressources allouées, tandis qu’un >fork< (par exemple l’appel d’une commande depuis un interpréteur) crée une nouvelle coquille avec copie de l’environnement sans cloner l’allocation de ressource, y charge le code à executer, crée le BCP, et lance l’execution de ce sous shell. L’appel d’un script fait un fork de l’interpréteur appelant dans lequel est censé s’interpréter ce script, et l’utilisation du shebang correspond juste à un exec pour charger un nouvel interpréteur dans ce sous shell à la place du fils cloné de l’interpréteur appelant.
Voili voilou.

@ripat > Je n’ai jamais dis le contraire. Tout shell qui se respecte peut s’appeller sous le nom sh et a ce moment là s’execute d’une manière conforme à la norme posix et donc portable.

Ma question c’était la comparaison se faisait sur un script lancé en ksh face à un bash ou entre deux sh mais lancé l’un avec l’exécutable bash et l’autre avec ksh (encore une fois je pose une question au sujet de perf et pas de fonctionalités).

[quote=“MisterFreez”]@ripat > Je n’ai jamais dis le contraire. Tout shell qui se respecte peut s’appeller sous le nom sh et a ce moment là s’execute d’une manière conforme à la norme posix et donc portable.[/quote]Tu peux effectivement l’appeler comme tu veux. C’est le shebang qui fera la différence. Tu peux appeler un script toto.sh, s’il comporte un shebang #!/usr/bin/ksh, il s’exécutera comme script ksh. Quant à la portabilité, ça dépend. Si tu utilises par exemple la syntaxe if [[ condition ]]; then… qui est supportée tant par bash que ksh, ce n’est pas POSIX, donc moins portable. Sur ubuntu, par exemple, le /bin/sh pointe vers un shell POSIX ash (dash) qui ne supporte pas cette syntaxe. Donc, si tu fais un script avec le shebang classique #!/bin/sh sur Debian, pas de problème puisque /bin/sh pointe par lien symbolique vers /bin/bash mais sur Ubuntu, il plantera. Ceci dit, pour tes scripts perso, la portabilité, tu t’assieds dessus! Tu cherches avant tout la lisibilité et la performance non?

[quote=“MisterFreez”]Ma question c’était la comparaison se faisait sur un script lancé en ksh face à un bash ou entre deux sh mais lancé l’un avec l’exécutable bash et l’autre avec ksh (encore une fois je pose une question au sujet de perf et pas de fonctionnalités).[/quote]Ça ne fait pas de différence. Si tu lances un script ksh depuis un shell zsh, bash ou csh il s’exécutera aussi vite car il s’exécutera dans un sous-process ksh. Sauf si tu le “source” depuis bash ou csh avec $ . script au lieu de $ ./script. Dans le premier cas, il exécutera les commandes du script comme si tu les avais tapées dans le shell parent. Dans le deuxième, il “ouvre” un shell ksh (process) et il y exécutera les commandes du script.

Encore une fois, je ne suis pas un spécialiste des shell, je fais simplement part de quelques observations.

[quote=“mattotop”]L’appel d’un script fait un fork de l’interpréteur appelant dans lequel est censé s’interpréter ce script, et l’utilisation du shebang correspond juste à un exec pour charger un nouvel interpréteur dans ce sous shell à la place du fils cloné de l’interpréteur appelant.[/quote]Ton explication dépasse un peu (!) mes connaissances en matière de shell. Je n’ai retenu que la dernière partie qui apporte également une réponse plus technique à MisterFreez. C’est le shebang qui fait tout.

[code]#!/bin/bash

function ma_fonction {
typeset VAR="Variable Interne"
echo 'depuis la fonction $VAR --> ’ $VAR
}

     ma_fonction "Appel de fonction"
     echo 'hors de la fonction $VAR --> ' $VAR[/code]

poiuy@debian:~$ ./essai depuis la fonction $VAR --> Variable Interne hors de la fonction $VAR -->
:smt002

Effectivement, faudrait que j’utilise une version plus récente du bash moi! :wink:

Comme quoi il ne faut jamais rester sur les apriori d’une ancienne version. Au niveau de la vitesse d’exécution, je maintiens. Du moins dans des traitement conséquents. Ainsi que l’avantage des tableaux à indices associatifs comme par exemple:

[code]#!/bin/ksh

typeset -A mesStations
mesStations[toto]="192.168.10.1"
mesStations[tata]="192.168.10.2"
mesStations[titi]=“192.168.10.3”

echo “IP de tata est ${mesStations[tata]}”

for station in “${!mesStations[@]}”; do
print "Poste $station —> ${mesStations[$station]}"
done
[/code]

Je pense me souvenir d’une différence importante avec les paramètres de position $1, $2 etc… et les fonctions mais cette fois je testerai dans un bash récent et “je vous dis quoi” comme on dit aussi chez nous!