Question The powers hell ou la puissance de l'enfer
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
- Messages : 6302
- Remerciements reçus 68
Cela fait la même chose mais ici pas besoin de coder le mécanisme, il est natif via le mot clé reference qui indique au compilateur une fonction anonyme.<br><br>Message édité par: Laurent Dardenne, à: 5/11/08 16:32program Anonyme1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
FDecorateur = reference to function (const Value: String) : String;
function Decorateur(Decoration : String):FDecorateur;Overload;
begin
result:= function(const Texte : String) : String
begin
result:=Decoration + Texte + Decoration;
end;
end;
function Decorateur(Debut : String; Fin : String):FDecorateur;Overload;
begin
result:= function(const Texte : String) : String
begin
result:=Debut + Texte + Fin ;
end;
end;
var CSharpCommente,
DelphiCommente,
CCommente : FDecorateur;
Text : String;
begin
try
DelphiCommente:= Decorateur('//');
CSharpCommente:= Decorateur('#');
CCommente:= Decorateur('/*', '*/');
Text:='Texte à commenter';
Writeln(DelphiCommente(Text));
Writeln(CSharpCommente(Text));
Writeln(CCommente(Text));
readln;
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
- Messages : 6302
- Remerciements reçus 68
A propos des fonctions locales
Il reste d’autres aspects à aborder sur les closures, notamment l’usage de fonction locale.
Le script présenté ici ne capture pas les fonctions locales d’une fonction utilisant une closure, c’est à dire :
[code:1]
function M{
[int] $a=0
[int] $x=0
function local:F {write-host \"Dans F\"
return $a
}
write-host \"Dans M\"
return closure {
write-host \"Closure\"
$a=1
$x=F
$x
}
}
$ClosureM=M
&$ClosureM
[/code:1]
Le terme « F » n'est pas reconnu en tant qu'applet de commande, fonction, programme exécutable ou fichier de script.
Pour cette écriture la fonction F n’est plus accessible lors de l’exécution de la closure.
Comme expliqué sur cette page: www.liafa.jussieu.fr/~hf/verif/ens/cours/C++/node17.html
Bien que la fonction M.F soit locale il n’est pas possible de le savoir car un objet fonction ne renseigne pas sa propriété Options (System.Management.Automation.ScopedItemOptions) :
[code:1]
write-host \"Dans M\"
@(dir function:M;dir function:F)|% {$_.name+ \" : \" + $_.Options}
[/code:1]
On peut déclarer la fonction F en globale :
[code:1]
$A=dir|%{$_.name}
function F {write-host \"Dans F\"
return $a
}
function M{
[int] $a=5
[int] $x=0
write-host \"Dans M\"
Write-host (F)
return closure {
write-host \"Closure\"
$a=1
$x=F
$x
}
}
$ClosureM=M
&$ClosureM
F
[/code:1]
Ce n’est pas forcément souhaitable, reste à la solution de l’inclure dans la closure :
[code:1]
Remove-item Function:f
function M
{
[int] $a=5
[int] $x=0
write-host \"Dans M\"
return closure {
function F #Déclarée Implicitement en locale
{
$y=$x
write-host \"Dans F a =$a x =$x y =$y\"
return $a*2
}
write-host \"Closure\"
$a=10;
$x=F;
F|out-null
}
}
$ClosureM=M
&$ClosureM
F
[/code:1]
Malheureusement avec cette approche la fonction M n’a plus accès à la fonction F. Quelques fois sous PowerShell la résolution de problèmes en crée de nouveaux.
Une solution moyennement satisfaisante serait de prendre en charge la redéclaration de la fonction F dans le code de la closure :
[code:1]
function Set-Function ( [String] $FunctionName, $CodeClosure){
#concaténe le code d'une fonction avec le code d'une closure.
# On renvoi un scriptbloc.
# On utilise une here-string pour l’expansion des
# différentes définitions.
return $ExecutionContext.InvokeCommand.NewScriptBlock(
@\"
function $FunctionName {
$((dir function:$FunctionName).Definition)
}
$CodeClosure
\"@ #here-string
)#NewScriptBlock
}#Set-Function
[/code:1]
Ensuite la déclaration de la closure s’en trouve modifié :
[code:1]
function M{
[int] $a=0
[int] $x=0
function F {write-host \"Dans F\"
return $a
}
write-host \"Dans M\"
return closure (
#Création de la function F dans le code de la closure
Set-Function \"F\" $('
#Définition du code de la closure
write-host \"Closure\"
$a=1
$x=F
$x
')
)
}
[/code:1]
Bon je m’arrête ici car PowerShell c’est un peu comme avec les cacahuètes, on sait où ça commence jamais où ça s’arrête
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
- Messages : 6302
- Remerciements reçus 68
C'est déjà plus \"simple\"
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
- Messages : 6302
- Remerciements reçus 68
Closure sur une fonction déclarant un paramètre :
[code:1]
# le précédent code :
# function NewDecorator([string]$decoration) {
# return closure {
# param ([string]$text)
# return $decoration + $text + $decoration;
# };
# }
#Devient
#
function NewDecorator([string]$decoration) {
return {
param ([string]$text)
return $decoration + $text
}.GetNewClosure();
}
$sharpDecorator = NewDecorator '//'
$sharpDecorator
&$sharpDecorator \"Ligne\" #émet //Ligne
[/code:1]
Closure sur une fonction déclarant deux paramètres :
[code:1]
function CDecorator([string]$Begin, [string]$End) {
return {
param ([string]$text)
return $Begin + $text + $End;
}.GetNewClosure();
}
$CDecorator=CDecorator \"/*\" \"*/\"
$CDecorator
&$CDecorator \"Commentaire\"; #émet /*Commentaire*/
[/code:1]
On obtient donc pour ce type de fonction le même comportement que précédement (usage de la fonction Closure).
Voyons ce qu'il en est avec la fonction compteur.
[code:1]
#Out-string converti un objet en un tableau de chaîne,
# Write-host affiche l'objet sans conversion.
function NewCounter
{
$i = 255; #Variable(s) locale(s) pouvant être capturée(s) par un appel à GetNewClosure()
#On renvoi un scriptblock
return {
#Ce scriptblock restitue, lors de son exécution, la valeur précédement capturée de $i
gv i|Out-String|Write-Host #Affiche i=255
$i++
gv i|Out-String|Write-Host #Affiche i=256
return $i
}.GetNewClosure()
#GetNewClosure() capture le ou les noms des variables et leur contenu à un instant t ($i = 255).
#GetNewClosure() renvoi un nouveau scriptbloc PLUS les variables mémorisées déclarées dans le méthode NewCounter.
#D'après MSDN : \"Any local variables that are in the context of the caller will be copied into the module.\"
}
$counter = NewCounter; #Emet dans le pipeline un unique objet de type scriptblock
&$counter; # $i= 255, renvoi 256
&$counter; # $i= 255, renvoi 256
#$i n'est pas déclaré dans la portée courante
$i
[/code:1]
Dans ce cas l'incrémentation de $i n'est pas prise en compte en dehors de la closure, c'est à dire dans le bloc de code suivant :
[code:1]
#closure mémorisant le nom et la valeur de $i
{
#Ce scriptblock restitue, lors de son exécution, la valeur précédement capturée de $i
gv i|Out-String|Write-Host
$i++
gv i|Out-String|Write-Host
return $i
}
[/code:1]
Dans la closure la valeur initial de $i persiste d'appel en appel, sa modification à l'intérieur du bloc de code constituant la closure n'est pas sauvegardée.
Son nom et son contenu sont mémorisés au moment de la construction de la closure et seulement à ce moment là. On prend donc une photo de cette variable.
Ajoutons une variable $j externe à notre closure:
[code:1]
$j=12 #on ne capture pas cette variable
function NewCounter
{
$i = 255;
return {
gv i,j|Out-String|Write-Host
$i++
$j++
gv i,j|Out-String|Write-Host
return $i
}.GetNewClosure()
}
$counter = NewCounter;
$j #Affiche le contenu de $j, c'est à dire 12
&$counter; #Appel la closure qui affiche 255,12 pour $i et $j.Puis 256,13 pour $i et $j
#Et renvoie 256
$j #Affiche le contenu de $j, c'est à dire 12
[/code:1]
Modifions $j à l'extérieur de la closure :
[code:1]
$j=789
&$counter; #Appel la closure qui affiche 255,789 pour $i et $j.Puis 256,790 pour $i et $j
$j # $j=789
[/code:1]
L'incrémentation de la variable $j est locale à la closure.
Sa suppression dans la portée courante impacte la closure.
[code:1]
remove-variable j
&$counter
[/code:1]
Allons un peu plus loin. La variable $j est toujours externe à notre closure modifions la désormais à l'intérieure de la closure :
[code:1]
$j=12 #on ne capture pas cette variable
function NewCounter
{
$i = 255; #Variable(s) locale(s) pouvant être capturée(s) par un appel à GetNewClosure()
$j=999
#On renvoi un scriptblock
return {
#Ce scriptblock restitue, lors de son exécution, la valeur précédement capturée de $i
gv i,j|Out-String|Write-Host
$i++
$j++ # variable de la portée courante. On capture cette variable
#$j
gv i,j|Out-String|Write-Host
return $i
}.GetNewClosure()
}
$counter = NewCounter;
$j #est égale à 12
&$counter; #Appel la closure qui renvoie 255,999 pour $i et $j.Puis 256,1000 pour $i et $j
#Renvoie 256
$j #est égale à 12
$j=200
&$counter; #Appel la closure qui affiche 255,999 pour $i et $j.Puis 256,1000 pour $i et $j
#Renvoie 256
$j #Affiche le contenu de $j, c'est à dire 200
[/code:1]
Bien qu'elle soit déclarée à l'extérieure de la fonction (NewCounter) retournant notre closure,
Ici la variable $j est également mémorisée car on crée une nouvelle variable locale dans la fonction NewCounter.
Sa suppression dans la portée courante n'impacte pas la closure.
[code:1]
remove-variable j
&$counter
[/code:1]
Reprenons le code précédent en y ajoutant l'émission de $J dans le pipeline :
[code:1]
$j=12 #on ne capture pas cette variable
function NewCounter
{
$i = 255; #Variable(s) locale(s) pouvant être capturée(s) par un appel à GetNewClosure()
$j=999
$j
#On renvoi un scriptblock
return {
#Ce scriptblock restitue, lors de son exécution, la valeur précédement capturée de $i
gv i,j|Out-String|Write-Host
$i++
$j++ # variable de la portée courante
gv i,j|Out-String|Write-Host
return $i
}.GetNewClosure()
}
$counter = NewCounter;
$j #est égale à 12
&$counter; #Appel la closure qui renvoie 255,999 pour $i et $j.Puis 256,1000 pour $i et $j
$j #est égale à 12
[/code:1]
On voit que l'appel à counter génére une erreur, bien que sa lecture dans la pipeline n'en génére pas:
[code:1]
$Counter|%{\"item = $_\"}
[/code:1]
Ceci est normal puisque dans ce cas on émet dans le pipeline un tableau d'objet(object0:999 ; objet1:scriptblock) et non plus un unique objet de type scriptblock :
[code:1]
$Counter.GetType()
[/code:1]
Déplaçons l'emission de $J dans la closure:
[code:1]
$j=12 #on ne capture pas cette variable
function NewCounter
{
$i = 255; #Variable(s) locale(s) pouvant être capturée(s) par un appel à GetNewClosure()
$j=999
#On renvoi un scriptblock
return {
#Ce scriptblock restitue, lors de son exécution, la valeur précédement capturée de $i
gv i,j|Out-String|Write-Host
$i++
$j++ # variable de la portée courante
$j
gv i,j|Out-String|Write-Host
return $i
}.GetNewClosure()
}
$counter = NewCounter;
$counter
$j #est égale à 12
&$counter; #Appel la closure qui renvoie 255,999 pour $i et $j.Puis 256,1000 pour $i et $j
$j #est égale à 12
[/code:1]
Cela ne génére plus d'erreur puisque désormais c'est la closure qui émet 2 objets.
Notre fonction doit donc renvoyer une seule valeur, à savoir une fermeture par l'appel à la méthode GetNewClosure.
Récapitulons
Première approche
[code:1]
#On fixe la valeur de la décoration via un paramètre
#La variable $décoration, externe à la fonction, N'A PAS D'IMPACT lors de la création d'un décorateur.
function NewDecorator([string]$decoration) {
return {
param ([string]$text)
return $decoration + $text
}.GetNewClosure();
}
$decoration='#'
#On crée un décorateur avec le contenu '//' indépendamment du contenu de la variable $decoration.
$sharpDecorator = NewDecorator '//'
&$sharpDecorator \"premier\"
$decoration='?'
&$sharpDecorator \"Second\"
[/code:1]
Seconde approche
[code:1]
#On fixe la valeur de la décoration selon le contenu de la variable $decoration.
#La variable $décoration, externe à la fonction, A UN IMPACT LORS de la création d'un décorateur.
function NewDecorator{
[string] $decoration=$decoration
return {
param ([string]$text)
return $decoration + $text
}.GetNewClosure();
}
[string] $decoration='//'
#On crée un décorateur avec '//', ce que contient la variable $decoration
$Decorateur1 = NewDecorator
&$Decorateur1 \"Premier //\" #Affiche //Premier //
$decoration='-'
#On crée un décorateur avec '#', ce que contient la variable $decoration
$Decorateur2 = NewDecorator
&$Decorateur1 \"Second-1 #\" #Affiche //Second-1 #
&$Decorateur2 \"Second-2 #\" #Affiche -Second-2 #
#La modification du contenu de la variable $décoration n'a pas d'impact sur les décorateurs créés précédemment
$decoration='?'
&$Decorateur1 \"Second-1 ?\" #Affiche //Second-1 ?
&$Decorateur2 \"Second-2 ?\" #Affiche -Second-2 ?
[/code:1]
Troisième approche
[code:1]
#On fixe la valeur de la décoration selon le contenu de la variable $decoration.
#La variable $décoration, externe à la fonction, a un impact A CHAQUE APPEL du décorateur.
#Dans ce cas la construction d'une closure n'est pas nécessaire, un simple scriptblock suffit.
function NewDecorator{
return {
param ([string]$text)
return $decoration + $text
}.GetNewClosure(); #Appel inutile dans ce cas
}
[string] $decoration='//'
#On crée un décorateur avec '//', ce que contient la variable $decoration
$Decorateur1 = NewDecorator
&$Decorateur1 \"Premier //\" #Affiche //Premier //
$decoration='-'
#On crée un décorateur avec '#', ce que contient la variable $decoration
$Decorateur2 = NewDecorator
&$Decorateur1 \"Second-1 #\" #Affiche -Second-1 #
&$Decorateur2 \"Second-2 #\" #Affiche -Second-2 #
#La modification du contenu de la variable $décoration n'a pas d'impact sur les décorateur crée précédemment
$decoration='?'
&$Decorateur1 \"Second-1 ?\" #Affiche ?Second-1 ?
&$Decorateur2 \"Second-2 ?\" #Affiche ?Second-2 ?
[/code:1]
La dernière approche démontre que tous les décorateurs suivent la valeur courante de la variable $Decoration, elle
n'est donc pas d'un grand intérêt.
La seconde utilisant un effet de bord est à mon avis potentiellement dangereuse.
Donc autant privilégier la première approche statique qui précise l'intention et facilite la maintenance.
[edit]
Voir aussi ces posts :
blogs.technet.com/b/heyscriptingguy/arch...p;utm_medium=twitter
www.nivot.org/2010/03/11/PowerShell20Par...tionsAndCmdlets.aspx
efreedom.com/Question/1-1827561/PowerShe...Parameters-Functions
www.dougfinke.com/blog/index.php/2010/12...igh-order-functions/
C# :
mikehadlow.blogspot.com/2011/07/what-is-closure.html
PowerShell Closure scenarios (class)
Message édité par: Laurent Dardenne, à: 5/04/14 10:30<br><br>Message édité par: Laurent Dardenne, à: 7/05/19 19:34
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
- Vous êtes ici :
- Accueil
- forum
- Le bistrot
- Discussions de comptoir
- The powers hell ou la puissance de l'enfer