4
$\begingroup$

Please ignore ^^^ possible duplicate (raised by me). The solution doesn't cover my case.

I'm trying to implement a simple hash table according to this top answer at "Is there HashTable structure in Wolfram Mathematica?"

That is, (copied from linked post)

(* Set some values in your table.*) 
table[a] = foo; table[b] = bar; table[c] = baz;

(* Test whether some keys are present. *)
{ValueQ[table[a]], ValueQ[table[d]]}
(* Out: {True, False} *)

(* Get a list of all keys and values, as delayed rules. *)
DownValues[table]
(* Out: {HoldPattern[table[a]] :> foo, HoldPattern[table[b]] :> bar, HoldPattern[table[c]] :> baz} *)

(* Remove a key from your table. *)
Unset[table[b]]; ValueQ[table[b]]
(* Out: False *)

Testing a key fails, however, when supplied from a variable, e.g.

table[a] = foo; table[b] = bar; table[c] = baz;
{ValueQ[table[a]], ValueQ[table[d]]}
(* Out: {True, False} *)

key = d; ValueQ[table[key]]
(* Out: True *)

ValueQ's documentation does state that it "returns True if any evaluation takes place".

How should I test keys held in variables?


EDIT

I voted to close this question having found a possible duplicate, but after trying both of @halirutan's solutions, I'm sad to report that neither work for my case. @halirutan's test cases involved numerics, while mine involves a symbol with a DownValue. So, I am still in search of a solution. Currently I am trying to decipher @halirutan's solutions so I can figure out where things are going wrong.

$\endgroup$
8
  • 1
    $\begingroup$ But why not just use Associations? $\endgroup$
    – march
    Apr 20, 2016 at 15:31
  • 1
    $\begingroup$ Actually, I also see that Association was introduced in version 10. I have version 9, so I'd still appreciate an answer for my case. $\endgroup$ Apr 20, 2016 at 15:35
  • 2
    $\begingroup$ I think your question is more fundamental than hash tables, it's really a question about how ValueQ works with respect to evaluation order. If you do ValueQ[Evaluate[key]] do you get the correct result? You should read up on Attributes specifically, HoldAll. $\endgroup$
    – N.J.Evans
    Apr 20, 2016 at 15:35
  • 1
    $\begingroup$ @N.J.Evans - You know, I actually tried that after I saw the HoldAll attribute, and sure, it appears to work, but I didn't have any confidence that it was actually working, since there's only two outcomes—True or False—and maybe some other effect was making it show the "correct" value. Please do post it as an answer if with your knowledge of how ValueQ works, Evaluate is a proper solution :-) $\endgroup$ Apr 20, 2016 at 15:38
  • 4
    $\begingroup$ Possible duplicate of ValueQ returns false positive for one argument type only $\endgroup$ Apr 20, 2016 at 16:05

1 Answer 1

3
$\begingroup$

tl;dr

Use this modified valueQ function.

SetAttributes[valueQ, {HoldFirst}];
valueQ[h_[args__]] :=
    With[{eval = args},
       ! Hold[Evaluate[h[eval]]] === Hold[h[eval]]
    ];

How ValueQ works.

I learned from @halirutan's answer in ValueQ returns false positive for one argument type only that ValueQ simply tests whether an expression is equal to running Evaluate on itself, i.e.

ValueQ[expr_] := !Hold[Evaluate[expr]] === Hold[expr];

If it does match up, then this non-transformation indicates non-value, and vice versa.

The issue with variables (DownValues).

So here's what was happening. Let's take @halirutan's example:

table[a] = foo;
table[b] = bar;
table[c] = baz;
{ValueQ[table[a]], ValueQ[table[d]]}

(* {True, False} *)

This is correct, of course—a is a key, d is not. Now, let's introduce keyA and keyD:

keyA = a;
keyD = d;
{ValueQ[table[keyA]], ValueQ[table[keyD]]}

(* {True, True} *)

Wait, but why? Well, let's plug in our expression into @halirutan's exposed ValueQ expression, i.e.

ValueQ[expr_] := !Hold[Evaluate[expr]] === Hold[expr];

For the keyA case, here are the evaluated and non-evaluated sides, separately:

{Hold[Evaluate[table[keyA]]], Hold[table[keyA]]}

(* {Hold[foo], Hold[table[keyA]]} *)

So, you see, table[keyA] is fully evaluated to foo on the LHS (as expected), while table[keyA] stays as is on the RHS (as expected), and the fact that they're different means ValueQ should return True, i.e. the expression indeed "has a value," transforming when evaluated.

Now, let's do the same for keyD:

{Hold[Evaluate[table[keyD]]], Hold[table[keyD]]}

(* {Hold[table[d]], Hold[table[keyD]]} *)

Here we see the issue. Even though table[d] doesn't have a value, the evaluation of keyD to d is still considered a transformation. Therefore when compared to the RHS, which has remained table[keyD], the difference leads to ValueQ returning True.

Evaluating both sides.

This was the first thing I tried, e.g.

{ValueQ[Evaluate[table[a]]], ValueQ[Evaluate[table[d]]]}

(* {False, False} *)

But now that we know how ValueQ works, it becomes a silly solution. By Evaluateing the expression prior to passing it into ValueQ, we guarantee that it will match during the comparison, since the very thing that ValueQ does is Evaluate the expression, making ValueQ return False always (i.e. the expression didn't transform, so it "has no value").

Finally, the solution.

@halirutan's two answers actually cover a more sophisticated problem than mine, but also different enough that neither cover the transformation of variables (i.e. symbols with DownValues).

It was pretty easy to come up with a solution, however. We simply need the argument (the "key" expression) to be evaluated before being passed into ValueQ:

SetAttributes[valueQ, {HoldFirst}];
valueQ[h_[args__]] :=
    With[{eval = args},
       ! Hold[Evaluate[h[eval]]] === Hold[h[eval]]
    ];

This appears to work fine:

{valueQ[table[keyA]], valueQ[table[keyD]]}

(* {True, False} *)
$\endgroup$

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.