Le renvoi d'une expression à partir d'une fonction diffère lorsque l'espace de noms est utilisé.Bonjour à tous,
J'ai rencontré un comportement étrange lors du renvoi d'une expression à partir d'une fonction et je n'ai déjà trouvé aucune discussion à ce sujet. J'ai l'habitude d'utiliser des espaces de noms pour à peu près tout. J'ai trouvé que lorsqu'une fonction renvoie une expression, l'expression n'est pas évaluée pour une fonction non délimitée, mais est évaluée si un espace de noms est utilisé. Est-ce que quelqu'un sait ce qui se passe ici?
Ce script doit montrer que la variable x n'est pas incrémentée lorsque la première fonction est appelée, mais est incrémentée lors du deuxième appel de fonction. J'utilise JMP v17.0.0, donc cela a peut-être été corrigé, mais je ne suis pas en mesure de mettre à jour pour le moment.
NewNamespace("varNamespace");
varNamespace:x = 0;<BR />
myFunc = Function({},
{DEFAULTLOCAL},
returnVal = Expr(varNamespace:x++);
NameExpr(returnVal);
);
myFunc();
Show(varNamespace:x);
NewNamespace("functionTest");
functionTest:myFunc = Function({},
{DEFAULTLOCAL},
returnVal = Expr(varNamespace:x++);
NameExpr(returnVal);
);
functionTest:myFunc();
show(varNamespace:x);Automatisation et scriptRe : Le renvoi d'une expression à partir d'une fonction diffère lorsque l'espace de noms est utilisé.J'ai mis à jour vers 17.2.0 et je peux confirmer que ce problème persiste.
Voici un exemple simplifié qui devrait, espérons-le, être plus facile à lire
x = 0;
myFunc = Function({}, {DEFAULTLOCAL},
Expr(::x++);
);
myFunc(); // Returns unevaluated expression: Expr(::x++)
Show(::x); // ::x=0;
NewNamespace("funcNS");
funcNS:myFunc = Function({},{DEFAULTLOCAL},
Expr(::x++);
);
funcNS:myFunc(); // Returns evaluated expression;
show(::x); // ::x=1;Re : Le renvoi d'une expression à partir d'une fonction diffère lorsque l'espace de noms est utilisé.Je suis presque sûr que vous avez découvert un bug. Voici une démo encore plus simplifiée de deux façons d'utiliser les fonctions dans les espaces de noms :
funcNS = New Namespace(
"funcNS",
f1 = Function( {},
expr(1+2)
)
);
funcNS:f2 = Function( {},
expr(1+2)
);
Show( Type( funcNS:f1() ), Type( funcNS:f2() ) );
Type(funcNS:f1()) = "Expression";
Type(funcNS:f2()) = "Nombre" ;
f1 renvoie la bonne réponse ; f2 évalue incorrectement la réponse. Je préfère la méthode f2 (sauf pour ce bug) car elle est plus rapide :
funcNS = New Namespace(
"funcNS",
f1 = Function( {},
1
)
);
funcNS:f2 = Function( {},
1
);
nf1 = 0;
For( endtime = HP Time() + 1e6, endtime > HP Time(), nf1++,
funcNS:f1()
);
Show( nf1 ); // 180K calls
nf2 = 0;
For( endtime = HP Time() + 1e6, endtime > HP Time(), nf2++,
funcNS:f2()
);
Show( nf2 ); // 260K calls
nf1 = 182239 ;
nf2 = 258929 ;
Les deux problèmes sont probablement liés. Les éléments spécifiés dans l'appel de fonction d'espace de noms ne sont pas construits tant qu'ils ne sont pas utilisés (ils sont conservés sous forme d'expressions.) Cela signifie que f1 n'est pas compilé lorsque l'espace de noms est créé, mais f1 est compilé à chaque fois qu'il est utilisé, donc plus lentement.Il existe donc probablement du code C++ spécialisé pour recompiler f1 puis l'évaluer. Je suppose que le même code est déclenché pour f2, mais n'est pas recompilé car f2 est déjà compilé, puis évalue incorrectement le résultat de f2, qui la plupart du temps est une valeur primitive et personne ne le remarque. La valeur expr() renvoyée est évaluée en une valeur plus simple, ce qui vous fait remarquer.
Voir plus...
@julien - je suis presque sûr que c'est un bug. Je ne pense pas qu'il y aura beaucoup d'utilisateurs d'espaces de noms en fonction d'un mauvais comportement.
Re : Le renvoi d'une expression à partir d'une fonction diffère lorsque l'espace de noms est utilisé.Y a-t-il eu une mise à jour à ce sujet ?
Pour autant que je sache, ce comportement existe toujours dans jmp 19.
Il semble clairement que la valeur de retour soit évaluée deux fois dans la définition ns:f2 :
ns = New Namespace("fns",
f1 = Function({},
x = Expr(1+3);
return(Name Expr(x));
);
);
f1 = Function({},
x = Expr(1+3);
return(Name Expr(x));
);
ns:f2 = Function({},
x = Expr(1+3);
return(Name Expr(x));
);
ns:f3 = Function({},
x = Expr(1+3);
return(Name Expr(Name Expr(x)));
);
Show(f1(), ns:f1(), ns:f2(), ns:f3());
f1() = 1 + 3;
ns:f1() = 1 + 3;
ns:f2() = 4;
ns:f3() = 1 + 3;
f1() = 1 + 3;
ns:f1() = 1 + 3;
ns:f2() = 4;
ns:f3() = x;
Si quelqu'un d'autre rencontre ce problème, la solution de contournement rapide est celle suggérée dans l'exemple : Enveloppez la valeur de retour dans « Name Expr » jusqu'à obtenir le résultat souhaité.
Cela ne fonctionne pas. J'avais défini ax dans la portée globale/ici lors des exécutions précédentes. Il semble impossible de renvoyer une expression à partir d'une fonction définie comme ns:f2.
Cela ressemble définitivement à un bug, je ne pense pas que quiconque s'attendrait à ce qu'un ns:f2 renvoie une valeur différente par rapport à ns:f1 ou f1 ; le code de la fonction est exactement le même.
Re : Le renvoi d'une expression à partir d'une fonction diffère lorsque l'espace de noms est utilisé.Je ferai un rapport via l'équipe de support.
TS-00235798
Re : Le renvoi d'une expression à partir d'une fonction diffère lorsque l'espace de noms est utilisé.J'ai également fait un rapport : TS-00235515
La réponse du support a été que cela n’était pas considéré comme un bug, même si le comportement était inattendu.
La cause première était celle que vous avez décrite ; en gros, la fonction ns:f2 est évaluée deux fois.
Cela semble également affecter tout ce qui est défini en dehors de la création initiale de l'espace de noms, comme on peut le voir dans l'exemple mis à jour ci-dessous.
La solution proposée consiste à envelopper le tout dans une expression - ns:f2 = Expr(Function(....))
Names Default to Here(1);
delete symbols();
ns = New Namespace("fns",
{
f1 = Function({},{Default Local},
x = Expr(1+3);
return(Name Expr(x));
),
e1 = Name Expr(expr(1 + a))
}
);
f1 = Function({},{Default Local},
x = Expr(1+3);
return(Name Expr(x));
);
ns:f2 = Function({},{Default Local},
x = Expr(1+3);
return(Name Expr(x));
);
ns:f3 = Expr(Function({},{Default Local},
x = Expr(1+3);
return(Name Expr(x));
));
ns:e2 = Name Expr(expr(1 + a));
ns:e3 = Expr(Name Expr(expr(1 + a)));
show(ns:e1);
show(try(ns:e2, exception_msg));
show(ns:e3);
Show(f1(), ns:f1(), ns:f2(), ns:f3());
ns:e1 = 1 + a;
Try(ns:e2, exception_msg) = {"Nom non résolu : a"(1, 2, "a", a /*###*/)};
ns:e3 = 1 + a;
f1() = 1 + 3;
ns:f1() = 1 + 3;
ns:f2() = 4;
ns:f3() = 1 + 3;
Re : Le renvoi d'une expression à partir d'une fonction diffère lorsque l'espace de noms est utilisé.Cette solution de contournement est… regrettable. Heureusement, la plupart des utilisateurs n'essaient pas de renvoyer des expressions et n'en ont pas besoin.
Re : Le renvoi d'une expression à partir d'une fonction diffère lorsque l'espace de noms est utilisé.En outre, la plupart des aspects malheureux peuvent être atténués par le retour
expr(expr(1+1))
plutôt que de rendre la fonction entière
expr(fonction(...
funcNS = New Namespace(
"funcNS",
f1 = Function( {},
expr(1+2)
)
);
funcNS:f2 = Function( {},
expr(expr(1+2)) // <<<< extra expr wrapper
);
Show( Type( funcNS:f1() ), Type( funcNS:f2() ) ); // expr, expr
Re : Le renvoi d'une expression à partir d'une fonction diffère lorsque l'espace de noms est utilisé.Cette solution est en effet plus élégante, cependant elle ne semble fonctionner que lorsque vous pouvez écrire directement l'expression à renvoyer.
Si l'expression est stockée dans une variable et que nous l'enveloppons dans une Expr(), la valeur renvoyée est une expression avec le nom de la variable
Dans ce cas, il semble que la seule solution soit d'envelopper la valeur de retour dans une Name Expr() et la fonction entière dans une expr
funcNS:f5 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
Name Expr(x)
));
Show( funcNs:f4() ); // 1+2
L’un des grands dangers de cette situation est que la deuxième évaluation semble se produire en dehors de la portée locale de la fonction.
Une variable globale pourrait masquer la valeur de retour, ce qui ne donnerait pas d'erreur.
Names Default to Here(1);
Delete Symbols(); // Only use if you dont have variables you need
funcNS = New Namespace("funcNS");
funcNS:f1 = Function( {}, {Default Local},
x = Expr(1+2);
Expr(x);
);
funcNS:f2 = Function( {}, {Default Local},
x = Expr(1+2);
Expr(Expr(x));
);
funcNS:f3 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
x;
));
funcNS:f4 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
Expr(x);
));
funcNS:f5 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
Name Expr(x)
));
Show( Try(funcNs:f1(), exception_msg) ); // Name Unresolved
Show(funcNs:f2()); // X
Show(funcNs:f3()); // 3
Show(funcNs:f4()); // x
Show(funcNs:f5()); // 1+2
// Second Evaluation happens outside Function scope
x = Expr(Expr(1+10));
Show(funcNs:f1()); // 1+10
Re : Le renvoi d'une expression à partir d'une fonction diffère lorsque l'espace de noms est utilisé.Ouah!
ajouté à Tiny Traps dans Jmp et JSL
Re : Le renvoi d'une expression à partir d'une fonction diffère lorsque l'espace de noms est utilisé. Names Default to Here(1);
x= Expr("Hello");
funcNS = New Namespace("funcNS");
funcNS:f = Function( {}, {Default Local},
x = Expr(1+2);
Substitute(Expr(Expr(ex)), Expr(ex), (Name Expr(x)))
);
Show( funcNS:f() , Type( funcNS:f() ) ); // expr, expr
- donnez à la fonction une chance de rechercher x à l'intérieur de la fonction : nommez Expr() au lieu de Expr().
- pour empêcher l'espace de noms d'évaluer la valeur de retour « deux fois », la valeur de retour doit être protégée avec Expr()
- cela rend la vie difficile pour le nom Expr()
Expr(Nom Expr()) empêchera Nom Expr() de faire son travail
- Dans 99 % des cas, on peut utiliser Évaluer Expr().
Mais ici c'est difficile, car Expr() est utilisé dans JMP à 2 fins :
- "c'est ici()" à l'intérieur de Évaluer l'expression()
- Je te protège() - comme solution au problème de l'espace de noms
malheureusement, Évaluer l'expression() sera satisfait du premier Expr() et l'évaluera.
donc, Évaluer Expr(Expr(Expr(Nom Expr(x)))) ne fonctionnera pas dans l'esprit de
Eval Expr (Je te protège (c'est ici(Nom Expr(x))))
au lieu de cela, il l'interprète comme
Eval Expr( c'est ici(je te protège(nom Expr(x))))
et donc il reviendra
Expr(Nom Expr(x))
... et ensuite JMP recherchera x en dehors de la portée de la fonction
- La solution ultime à tous les problèmes d'expression est Substitute()
Re : Gestion des expressions dans JMP : astuces et trappes
- is there an easier solution than
Substitute(Expr(Expr(ex)), Expr(ex), (Name Expr(x)))