8
$\begingroup$

I often import several long time series to analyze something about a city. When I have multiple cities, I use contexts. As a simple example,

Begin["paris`"];
dataRaw=Import["Paris.csv"];
data=Differences[dataRaw];
dataMax=Max[dataRaw];
End[];
Begin["madrid`"];
dataRaw=Import["Madrid.csv"];
data=Differences[dataRaw];
dataMax=Max[dataRaw];
End[];

Now I can for instance do plots, so

ListLinePlot[{paris`data,madrid`data}]

and this seems much less sloppy than keeping everything in the Global context and manually adding the city name to the end of every symbol name.

But I would like to write a function to take care of the importing and processing. Naively,

importCity[cityName_String] := (
    Begin[ToLowerCase[cityName]~~"`"];
    dataRaw=Import[cityName~~".csv"];
    data=Differences[dataRaw];
    dataMax=Max[dataRaw];
    End[];
)

but this doesn't appear to create the context as intended. If contexts are a good way of organizing this sort of thing, how can I make a function that creates a context and puts results in it? And if this is an abuse of contexts, what's the proper way to do this?

$\endgroup$

1 Answer 1

9
$\begingroup$

I think, using contexts here is a sensible suggestion, particularly because you want to use several variables. One possible alternative is to set up a struct-like data structure, where encapsulation mechanism is based on Module-generated persistent variables. There were many discussions related to emulation of structs in Mathematica, but, given the encapsulation aspect, I will refer you to this answer. Contexts do have an advantage that the symbols encapsulated with them can be easily serialized (e.g. with Save or DumpSave), but a disadvantage that it is not clear how to construct these long variable names automatically without using strings (should you wish to automate things).

In any case, the reason that your function does not work is because the parsing is done first, before any code executes, and all your symbols are already created in the current (most likely Global`) context, so these are used also inside Begin and End. While I don't see how you can avoid creation of such symbols (except explicit use of Remove, or if you use your code in a string form), here is a work-around to create the symbols you need in a proper context:

ClearAll[contextWrap];
SetAttributes[contextWrap, HoldFirst];
contextWrap[code_, context_String] :=
   With[{boxed = MakeBoxes[code]},
     Block[{$ContextPath},
       BeginPackage[context];
         Quiet@ReleaseHold@MakeExpression@boxed;
       EndPackage[];
     ]
  ]; 

Basically, this converts the code into the boxed form, and delays its parsing until runtime. I used similar technique in this answer. Here is an example:

contextWrap[a = 1; b = 2; c = 3, "MyContext`"]

MyContext`a

(* 
   1
*)

There is no shadowing here because the context MyContext` was not kept on the $ContextPath. So, in your case, you'd need something like

contextWrap[
   dataRaw=Import[cityName~~".csv"];
   data=Differences[dataRaw];
   dataMax=Max[dataRaw];,
   ToLowerCase[cityName]~~"`"
]

If you absolutely want to avoid the side effect of creation of new symbols in the current context, I can refer you to the hack based on $PreRead, which I posted in this MathGroup thread.

$\endgroup$
2
  • $\begingroup$ This does what I'm looking for. Exactly how contextWrap works is currently beyond me, but that won't stop me from using it. Thanks. $\endgroup$ Commented Mar 15, 2012 at 15:32
  • $\begingroup$ @ArgentoSapiens Glad I could help. Thanks for the accept. $\endgroup$ Commented Mar 15, 2012 at 15:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.