2011-04-20 3 views
1

Y at-il une meilleure façon de le faire que:Modification du contenu d'une fermeture

|aBlock| 
aBlock := [3+2]. 
aBlock := Object readFrom: (a printString copyReplaceAll: '3' with: '2'). 

?

EDIT Ce code était juste un exemple, qu'en des choses comme:

[:something | 
    something checkSomethingElse ifNil: 
     [whatever] 
    ifNotNil: 
     [something getSomethingDone]] 

Où maintenant je veux checkAnotherThing au lieu de checkSomethingElse.

ou:

[:oneParameter :anotherParameter | 
    oneParameter doSomethingWith: anotherParameter] 

Où maintenant je veux ajouter un troisième paramètre et:

[:oneParameter :anotherParameter :yetAnotherParameter | 
    oneParameter doSomethingWith: anotherParameter and: yetAnotherParameter] 
+0

Pourquoi faites-vous cela? – mathk

+0

Parce que, dans Smalltalk, je peux: P –

+0

Vous pouvez utiliser #perform: et co. changer la méthode de test. D'où: '[: quelque chose: selectorCheck | (quelque chose effectuer: selectorCheck) ifNil: [] ...] '. – mathk

Répondre

2

Sérialiser un bloc en tant que chaîne et effectuer des manipulations de chaîne, bien que pratique, est également très dangereux si vous n'avez pas une idée très précise du contenu du bloc.

Il semble que vous aimeriez pouvoir manipuler l'AST du bloc - à partir d'un bloc, l'analyser, changer la structure (en remplaçant le littéral, dans ce cas) et ensuite compiler la structure modifiée. À cette fin, vous pouvez faire quelque chose comme ceci:

| aBlock ast | 
aBlock := [3+2]. 
ast := aBlock decompile. 
ast statements first receiver: (DecompilerConstructor new codeAnyLiteral: 4). 
aBlock := (Compiler evaluate: ast printString) first. 
aBlock value. "==> 6" 

Notez que nous ne sommes pas réellement altérer unBloc, mais la création d'une copie mutée de unBloc.

Le principe s'applique plus généralement: décompiler le bloc, faire votre manipulation (changer le sélecteur à mi-chemin d'une chaîne d'envois de messages, par exemple), compiler le nouvel arbre d'analyse. (Je ne sais pas comment compiler l'arbre directement plutôt que d'évaluer l'arbre imprimé, mais je suis sûr qu'il y a un moyen.)

(Avertissement: J'ai écris ce qui précède dans Squeak Je ne connais pas l'état d'avancement d'Opal, le nouveau compilateur de Pharo, alors peut-être que vous feriez quelque chose de légèrement différent dans Pharo.)

+0

C'est ce que je recherchais, merci beaucoup :) –

2
In Pharo: 

| aBlock x | 

x := 1. 

aBlock := [ x := x + 1]. 

Transcript show: aBlock value printString; cr. 

x := 41. 

Transcript show: aBlock value printString; cr. 
+0

Cela ne modifie pas du tout le bloc ...Je voulais dire modifier comme dans MyClass compile: aMethod, mais pour une fermeture. –

+0

Bien sûr, il ne modifie pas le bloc, mais il modifie le comportement du bloc existant. Et si quelqu'un détient une référence au bloc, il observera un comportement différent entre les évaluations, ce qui est pour lui à peu près le même que si le bloc avait changé. Notez que je choisirais rarement un tel comportement dans mon code, et préférerais beaucoup le bloc d'usine comme Lukas l'a suggéré. Les blocs qui changent leur comportement sentent les globales :) –

2

Bien sûr, vous pouvez utiliser la réflexion pour manipuler des blocs, mais la solution la plus propre est de lier dynamiquement les valeurs de votre bloc en l'entourant d'un autre bloc:

factory := [ :a :b | [ a + b ] ]. 

Le factory produit des blocs a et b sont liés à des valeurs différentes:

aBlock := factory value: 3 value: 2. 

évaluation aBlock réponses 5.

+0

Je devrais m'expliquer. Le code précédent n'était qu'un exemple, ce que je voulais dire, c'est comment utiliser la réflexion pour manipuler les blocs :) –