7

What is the best way to generate a name for some temporary context which is guaranteed to be unique (context with this name must not exist in the system)?

Alexey Popkov
  • 9,355
  • 4
  • 42
  • 93

5 Answers5

10

The following expression will generate a context name that is guaranteed not to conflict with any loaded context:

First@Contexts[] //.
  c_ /; MemberQ[Contexts[], c] :>
    "Context"~~ToString[RandomInteger[1000000]]~~"`"

It makes no attempt to account for contexts that are not yet loaded. As written, this expression could be used up to 1,000,000 times before running out of names. Adjust the fixed string ("Context") and name count (1000000) to suit your taste.

Update

As @Leonid points out in a comment, empty contexts will not be listed in Contexts[]. Therefore, it is strictly speaking possible that this expression could return the name of an existing empty context.

UUIDs

For all practical purposes, generating a name from a number randomly selected from a large enough range would work, e.g.

"Context"~~ToString[RandomInteger[2^128]]~~"`"

In a similar vein, one could use a UUID. UUIDs are routinely used as identifiers that are phenomenally likely to be unique across all network nodes as well:

Needs["JLink`"]
LoadJavaClass["java.util.UUID"]

"Context"~~
  StringReplace[JavaBlock@java`util`UUID`randomUUID[]@toString[], "-" -> ""]~~
  "`"
WReach
  • 18,098
  • 3
  • 49
  • 93
  • 2
    It might be better to use [`$ModuleNumber`](http://reference.wolfram.com/mathematica/ref/$ModuleNumber.html) instead of random integer. That is the counter, that is automatically incremented when evaluated. – Sasha Jul 28 '11 at 15:42
  • @Sasha The possible issue with `$ModuleNumber$` is that it may not be unique across different mma sessions, which is a plausible scenario for a context name. – Leonid Shifrin Jul 28 '11 at 15:46
  • @Leonid Agreed, but `RandomInteger` approach suffers from the save drawback. – Sasha Jul 28 '11 at 15:48
  • @Sasha Yes, you are right (you probably meant "same", not "save"). I posted the answer which seems to solve this problem. – Leonid Shifrin Jul 28 '11 at 16:10
  • @Leonid I added a section concerning to UUIDs to address the multiple session issue. – WReach Jul 28 '11 at 16:36
  • @WReach Nice discussion of UUIDs - +1. – Leonid Shifrin Jul 28 '11 at 16:57
  • 2
    @Sasha A pity indeed. I have made a number of embarrassing typos and other grammar-related mistakes in my SO comments, and come across those every now and then, only to feel the same. – Leonid Shifrin Jul 28 '11 at 17:01
  • 2
    @WReach By the way, using `Contexts[]` isn't cheap, it takes a sizable fraction of a second (the rule involving `MemberQ[Contexts[],...]` in the `Condition` is especially wasteful in general, since `Contexts[]` will be recomputed for each potential match. Could use `With` to precompute. Not an issue here though, as the rule is applied to an atom). Whether or not this is acceptable depends of course on the application. – Leonid Shifrin Jul 28 '11 at 17:59
  • 1
    @WReach While answering a related question: http://stackoverflow.com/questions/6867575/is-it-possible-to-delete-context-from-the-list-of-loaded-contexts/, I realized that `Contexts[]` only stores context names for non-empty contexts (containing symbols). Therefore, some empty contexts may e.g. be present on the `$ContextPath` and in `$Packages`, but not in `Contexts[]`. This looks to me as yet another argument against basing the unique context name generator on `Contexts[]` (apart from the performance issue I noted earlier). – Leonid Shifrin Jul 29 '11 at 13:47
  • @Leonid Thanks for the info. I have updated my answer to reference your comment. I've come to the conclusion that if I had this requirement (which I don't), I would use the UUID approach as it is near-enough guaranteed to generate a context name that is unique over all space and for all time. – WReach Jul 29 '11 at 14:13
7

I can suggest a function I used here:

Clear[unique];
unique[sym_] :=
ToExpression[
   ToString[Unique[sym]] <> 
      StringReplace[StringJoin[ToString /@ Date[]], "." :> ""]];

You can replace the ToExpression by StringJoin[...,"`"] to tailor it to your needs.

Community
  • 1
  • 1
Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
  • 1
    This isn't guaranteed to be unique either, is it? Adding the date and time makes it very unlikely that you get a collision, but it still is a theoretical possibility. – Sjoerd C. de Vries Jul 28 '11 at 16:18
  • 2
    @Sjoerd Well, it goes to microseconds, plus it *does* use `Unique`, so you only have a few microseconds to end one session, start another one and generate there all the previous unique indices. Seems *very* unlikely to me :) – Leonid Shifrin Jul 28 '11 at 16:20
  • 5
    @Sjoerd I agree with the theoretical objection but I expect that the problem will not occur in practice. – WReach Jul 28 '11 at 16:22
2

Another option would be to look at all starting contexts (before the first backquote), find their string length and then generate a string (maybe random, but that isn't necessary) that is at least one character longer than the others. This is guaranteed to be unique, and there isn't even a theoretical possibility of a collision as in some of the other solutions.

sl = (StringSplit[#, "`"][[1]] & /@ Contexts[] // StringLength // Max )

Out[349]= 30

In[353]:= "c" ~~ ToString[10^sl] ~~ "`"

Out[353]= "c1000000000000000000000000000000`"

A disadvantage of this method would be that the context names get longer after each repeated application of this method. ;-) If that's a problem we could create a unique name based on the set of longest context names using Cantor's diagonal procedure.

Sjoerd C. de Vries
  • 16,122
  • 3
  • 42
  • 94
0

Is Unique perhaps what you're looking for?

Sjoerd C. de Vries
  • 16,122
  • 3
  • 42
  • 94
  • `Unique` would guarantee unique symbol, but not the context. `Unique` is appending `$ModuleNumber` to the symbol name. – Sasha Jul 28 '11 at 16:01
  • @Sjoerd Not, `Unique` may generate a name that coincide with already existing context. – Alexey Popkov Jul 28 '11 at 16:02
  • @alexey You are right. `Unique` only generates unique strings or symbols, but not context names. You have to combine it with some additional code as Leonid does. – Sjoerd C. de Vries Jul 28 '11 at 17:35
0

This is really an example illustrating Alexey's response to Sjoerd's answer/question. From a fresh kernel on my machine, the following code

Begin["myContext3`"];
Unique["myContext"]

Yields "myContext3". Thus, clearly, Unique (the first thing I thought of) does not work.

Incidentally, I would have just added a comment to Sjoerd's response, but I don't know how to include the accent symbol used to denote a context inline. Does anyone here know how to do this?

Mark McClure
  • 4,862
  • 21
  • 34