Ciò che richiede è difficile. Questo è un lavoro per macro, come già esposto dagli altri. Esplorerò una possibilità diversa: usare gli stessi simboli ma inserire alcuni wrapper attorno al codice che vuoi scrivere. Il vantaggio di questa tecnica è che il codice viene trasformato "lessicalmente" e in "compile-time", piuttosto che in fase di esecuzione (come nelle altre risposte). In genere è più facile e veloce eseguire il debug.
Così, qui è una funzione che avrebbe trasformato la With
con la sintassi proposto:
Clear[expandWith];
expandWith[heldCode_Hold] :=
Module[{with},
heldCode /. With -> with //. {
HoldPattern[with[{{} = {}, rest___}, body_]] :>
with[{rest}, body],
HoldPattern[
with[{
Set[{var_Symbol, otherVars___Symbol}, {val_, otherVals___}], rest___},
body_]] :>
with[{{otherVars} = {otherVals}, var = val, rest}, body]
} /. with -> With]
Si noti che questo funziona con il codice in attesa. Questo ha il vantaggio che non dobbiamo preoccuparci di una possibile valutazione del codice né all'inizio né al termine di expandWith
. Ecco come funziona:
In[46]:= [email protected][With[{{x1,x2,x3}={a,b,c}},x+x1+x2+x3]]
Out[46]= Hold[With[{x3=c,x2=b,x1=a},x+x1+x2+x3]]
Questo non è, tuttavia, molto conveniente da usare.Ecco una funzione comodità per semplificare questo:
ew = Function[code, [email protected]@[email protected], HoldAll]
possiamo usarlo ora come:
In[47]:= [email protected][{{x1,x2}={a,b}},x+x1+x2]
Out[47]= a+b+x
Così, per far accadere l'espansione nel codice, basta avvolgere ew
intorno ad esso. Qui è il vostro caso per la definizione della funzione:
Remove[f];
ew[f[x_] := With[{{x1, x2} = {a, b}}, x + x1 + x2]]
Ora controllare e vedere che ciò che otteniamo è una definizione estesa:
?f
Global`f
f[x_]:=With[{x2=b,x1=a},x+x1+x2]
Il vantaggio di questo approccio è che si può avvolgere ew
attorno a un pezzo arbitrariamente grande del tuo codice. Quello che succede è che da esso viene generato il primo codice espanso, come se lo scrivessi tu stesso, e poi quel codice viene eseguito. Per il caso delle definizioni della funzione, come f
sopra, possiamo dire che la generazione del codice avviene in "tempo di compilazione", così eviterai qualsiasi sovraccarico di run-time quando userai la funzione in un secondo momento, che potrebbe essere sostanziale se la funzione viene chiamata spesso.
Un altro vantaggio di questo approccio è la sua componibilità: è possibile creare numerose estensioni di sintassi e per ognuna di esse scrivere una funzione simile a ew
. Quindi, a condizione che queste funzioni di trasformazione del codice personalizzate non siano in conflitto tra loro, è sufficiente comporle (annidarle) per ottenere un effetto cumulativo. In un certo senso, in questo modo crei un generatore di codice personalizzato che genera codice Mathematica valido da alcune espressioni Mathematica che rappresentano programmi nella tua lingua personalizzata, che puoi creare all'interno di Mathematica usando questi mezzi.
EDIT
Scrivendo expandWith
, ho usato l'applicazione regola iterativa per evitare di trattare con il controllo di valutazione, che può essere un pasticcio. Tuttavia, per chi è interessato, ecco una versione che fa un lavoro esplicito con pezzi di codice non valutati.
Clear[expandWithAlt];
expandWithAlt[heldCode_Hold] :=
Module[{myHold},
SetAttributes[myHold, HoldAll];
heldCode //. HoldPattern[With[{Set[{vars__}, {vals__}]}, body_]] :>
With[{eval =
(Thread[Unevaluated[Hold[vars] = Hold[vals]], Hold] /.
Hold[decl___] :> myHold[With[{decl}, body]])},
eval /; True] //. myHold[x_] :> x]
Lo trovo molto più complicato del primo però.
Rifletto spesso su tali scelte di progettazione e a volte pongo domande del genere. Sfortunatamente è raro che tali cose vengano spiegate. Mi sono opposto al fatto che Con/Block/Module funzionano in modo diverso, e che 'Set' nel primo argomento di questi non sta eseguendo l'operazione' Set', ma è invece la sintassi. –
Dalla mia lettura della documentazione del messaggio di errore (citata parte verso la fine della mia Q), mi sembra che ci sia un semplice pattern matching che guida il comportamento corrente --- cioè, accettando pattern che hanno la Head List, contenente solo elementi che valutano True per qualcosa come MatchQ [Hold [Set [x, 1]], Hold [Set [Symbol_, _]]]. Se così fosse, sembrerebbe possibile, almeno nel mio miope mondo, modificare il pattern matching/testing per consentire il comportamento che cerco, e che la documentazione su Set sembra implicare dovrebbe funzionare (come una forma valida di incarico). – telefunkenvf14
Non sono d'accordo, ma non conosco alcun modo per effettuare quel cambiamento. –