Question The powers hell ou la puissance de l'enfer

Plus d'informations
il y a 15 ans 5 mois #3164 par Laurent Dardenne
Pour info voici le même principe de la function decorator codé en Delphi 2009 :

program 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.

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:32

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 15 ans 5 mois #3185 par Laurent Dardenne
Allez, un dernier pour la route :P

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 \&quot;Dans F\&quot;
return $a
}
write-host \&quot;Dans M\&quot;

return closure {
write-host \&quot;Closure\&quot;
$a=1
$x=F
$x
}
}

$ClosureM=M
&amp;$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 \&quot;Dans M\&quot;
@(dir function:M;dir function:F)|% {$_.name+ \&quot; : \&quot; + $_.Options}
[/code:1]
On peut déclarer la fonction F en globale :
[code:1]
$A=dir|%{$_.name}
function F {write-host \&quot;Dans F\&quot;
return $a
}

function M{
[int] $a=5
[int] $x=0

write-host \&quot;Dans M\&quot;
Write-host (F)
return closure {
write-host \&quot;Closure\&quot;
$a=1
$x=F
$x
}
}

$ClosureM=M
&amp;$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 \&quot;Dans M\&quot;
return closure {
function F #Déclarée Implicitement en locale
{
$y=$x
write-host \&quot;Dans F a =$a x =$x y =$y\&quot;
return $a*2
}

write-host \&quot;Closure\&quot;
$a=10;
$x=F;
F|out-null
}
}
$ClosureM=M
&amp;$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(
@\&quot;
function $FunctionName {
$((dir function:$FunctionName).Definition)
}
$CodeClosure
\&quot;@ #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 \&quot;Dans F\&quot;
return $a
}
write-host \&quot;Dans M\&quot;
return closure (
#Création de la function F dans le code de la closure
Set-Function \&quot;F\&quot; $('
#Définition du code de la closure
write-host \&quot;Closure\&quot;
$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.

Plus d'informations
il y a 15 ans 3 semaines #4490 par Laurent Dardenne
Pour info, les closures sont implémentées dans le V2 .
C'est déjà plus \&quot;simple\&quot; :P

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 15 ans 3 semaines #4509 par Laurent Dardenne
Quelques remarques sur la création de closure à l'aide de la méthode GetNewClosure.

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
&amp;$sharpDecorator \&quot;Ligne\&quot; #é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 \&quot;/*\&quot; \&quot;*/\&quot;
$CDecorator
&amp;$CDecorator \&quot;Commentaire\&quot;; #é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 : \&quot;Any local variables that are in the context of the caller will be copied into the module.\&quot;
}

$counter = NewCounter; #Emet dans le pipeline un unique objet de type scriptblock
&amp;$counter; # $i= 255, renvoi 256
&amp;$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
&amp;$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
&amp;$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
&amp;$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
&amp;$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
&amp;$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
&amp;$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
&amp;$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|%{\&quot;item = $_\&quot;}
[/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
&amp;$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 '//'
&amp;$sharpDecorator \&quot;premier\&quot;
$decoration='?'
&amp;$sharpDecorator \&quot;Second\&quot;
[/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
&amp;$Decorateur1 \&quot;Premier //\&quot; #Affiche //Premier //

$decoration='-'
#On crée un décorateur avec '#', ce que contient la variable $decoration
$Decorateur2 = NewDecorator
&amp;$Decorateur1 \&quot;Second-1 #\&quot; #Affiche //Second-1 #
&amp;$Decorateur2 \&quot;Second-2 #\&quot; #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='?'
&amp;$Decorateur1 \&quot;Second-1 ?\&quot; #Affiche //Second-1 ?
&amp;$Decorateur2 \&quot;Second-2 ?\&quot; #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
&amp;$Decorateur1 \&quot;Premier //\&quot; #Affiche //Premier //


$decoration='-'
#On crée un décorateur avec '#', ce que contient la variable $decoration
$Decorateur2 = NewDecorator
&amp;$Decorateur1 \&quot;Second-1 #\&quot; #Affiche -Second-1 #
&amp;$Decorateur2 \&quot;Second-2 #\&quot; #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='?'
&amp;$Decorateur1 \&quot;Second-1 ?\&quot; #Affiche ?Second-1 ?
&amp;$Decorateur2 \&quot;Second-2 ?\&quot; #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.

Temps de génération de la page : 0.082 secondes
Propulsé par Kunena