9
$\begingroup$

I would like a table with rational indexes - thus it would be practical to use a dictionary, which, in Mathematica are implemented with the indexed variables. I would like to be able to do:

...
If[a[1/2] is defined, a[1/2] = a[1/2] + 1, a[1/2] = 1]
...
If[a[4568/8746] is defined, a[4568/8746] = a[4568/8746] + 1, a[4568/8746] = 1]
...

and then further along:

ConvertToList[a] -> {... {1/2, 4}, {4568/8746, 9}, ... }

Had my indexes been integers, I could have used the built-in function Array. But they are not, thus at some point, I need to get a list of all the indexes for which ahas a value. I could hack a solution (for instance: implement my own dictionary structure, or keeping a set of all values I defined and use it to know where I should look), but this seems silly. Especially since Mathematica provides the functionality I desire as a meta-function:

?a

Unfortunately I can't seem to make use of it programmatically.

How can I do what I want? Am I wrong to think that indexed variables are the way to do it?


UPDATE: Thank you so much for your extremely informative answers---I admit I wish I were able to accept both of them! Especially as they both give complementary information.

To give a little bit more information on my usage, I have both been looking for this general functionality for some time (a generic hash table type structure), but the example I suggested is a real-world example not a minimal one :)

As a first approach (because I can't get an easy formula), I am computing the probability distribution function of the ratio of two discrete random variables. To do this, I am iterating over all values of the first random variable, and then iterating over all values the second one can take (which depend on the value of the first random variable). I then complete the table using as index the ratio of the two random variables. The problem is there are great many rationals and that $1/2$ is the same as $2/4$, etc., hence the reason for using a hash table.

I had already managed a clunky way of doing this, but now I have a very fast function, thanks to you two, that allows me to get the PDF of my distribution. It is then convenient to convert this to a CDF of the distribution to avoid the many fluctuations inherent to the discrete rational distribution.

You are both deeply thanked for your explanations.

$\endgroup$
2
  • 2
    $\begingroup$ Have you seen DownValues[]? $\endgroup$ Commented Aug 15, 2012 at 2:55
  • 1
    $\begingroup$ You might also want to look at this answer by Oleksandr for creating a hash table. It might be helpful depending on the use case (and how often you query/update) $\endgroup$
    – rm -rf
    Commented Aug 16, 2012 at 14:42

2 Answers 2

9
$\begingroup$

The two key functions you need to know to do what you want are:

  • ValueQ to test if your symbol has a value or not and
  • DownValues to get a list of rules for the definitions (some kinds, anyway)

With these, here's a barebones implementation that should give you the idea. Here a is the database, f is the function to query values from a (and set it if not already defined) and g retrieves the indices and their associated values:

ClearAll[a, f, g]
f[x : _Integer | _Rational] /; ValueQ[a[x]] := a[x] += 1
f[x : _Integer | _Rational] := a[x] = 1
g[a_Symbol] := DownValues[a] /. (HoldPattern[_[_[x_]]] :> y_) :> {x, y}

Here's some sample usage:

(* index for which a is not defined *)
f[2]
(* 1 *) 

(* set value for a[3] *)
a[3] = 2;

(* query a[3] and update it in the process *)
f[3]
(* 3 *)

(* retrieve indices and their values *)
g[a]
(* {{2, 1}, {3, 3}} *)
$\endgroup$
3
  • $\begingroup$ I was in process of trying to answer when RMs answer appeared. An alternative is to define f[x_] := If[IntegerQ[a[x]], a[x] = a[x] + 1, a[x] = 1] in place of the two f's. This is closer to the original posters form and leads to the same kind of thing. This relies on the fact that the values of a[x] are always integers (if they're defined). $\endgroup$
    – bill s
    Commented Aug 15, 2012 at 3:34
  • 1
    $\begingroup$ @bills They can be rationals too, not just integers. If could be an alternative too, but I like this form for its extensibility (the question seemed like a very minimal example and not really a real world application) $\endgroup$
    – rm -rf
    Commented Aug 15, 2012 at 3:56
  • $\begingroup$ A side question about g[a_Symbol] := DownValues[a] /. (HoldPattern[_[_[x_]]] :> y_) :> {x, y}. Why is it not like (HoldPattern[_[x_]] :> y_) :> {x, y}, as Downvalues returns somthing like {HoldPattern[a[2]] :> 1}? $\endgroup$
    – Chromatic
    Commented Sep 4, 2015 at 14:53
8
$\begingroup$

Define a default or background for the hash

I would like to propose a different approach, which is to define a default or generic value for a.

If there is not a consistent background value you may use:

a[_] = "Empty";

If[a[1/2] =!= "Empty", a[1/2] += 1, a[1/2] = 1]

If you can define a constant (or structured) background for your table such as 1, you can simplify this to:

b[_] = 1;

b[1/2] += 1
b[3/4] -= 1.8
2

-0.8

Reading the "hash table"

To read the full hash table you can use DownValues (see section below) and one of several methods depending on your intent.

You might use a pattern like the one I used here to return a select set of values:

Cases[DownValues@b, (_@_@key_?NumericQ :> val_) :> {key, val}]
{{1/2, 2}, {3/4, -0.8}}

You might use Part to efficiently extract all values (if pattern-free keys are used the last rule will be the background b[_] so it is skipped with ;; -2):

{#[[;; -2, 1, 1, 1]], #[[;; -2, 2]]}\[Transpose] & @ DownValues @ b
{{1/2, 2}, {3/4, -0.8}}

Related information

The long form of ?a is Information[a], but this only prints the definition rather than returning it (it returns Null). Closer is ToString[Definition[a], InputForm] which returns the definition as printed by Information in the form of a String.

Deeper down you can access the Value lists such as OwnValues, DownValues, SubValues, UpValues, NValues, etc. Unlike Information and Definition these return lists of equivalent replacement rules.

(If you want to find the definition for a specific value I recommend my step evaluation function.)

Finally, you can check to see if an expression has a value with ValueQ, but be sure to read this:

$\endgroup$
3
  • 2
    $\begingroup$ You could use a default of a[_]=Sequence[] and the code then becomes ++a[1/2] $\endgroup$ Commented Aug 15, 2012 at 13:02
  • $\begingroup$ Thank you for the additional information on how to to access Value lists. $\endgroup$
    – Jérémie
    Commented Aug 16, 2012 at 14:32
  • $\begingroup$ @SimonWoods Sadly that won't work with SubtractFrom but it's great otherwise. Any clever solution to that problem? $\endgroup$
    – Mr.Wizard
    Commented Jun 25, 2014 at 13:53

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.