5
$\begingroup$
expr = {slot};
Position[expr, slot]
MapAt[f, expr, %[[1]]]

yields

{{1}}
{f[slot]}

as expected. However,

expr = slot;
Position[expr, slot]
MapAt[f, expr, %[[1]]]

yields

{{}}
slot

not

{{}}
f[slot]

as I'd expect. On the other hand, using

MapAt[f, slot, {{}}]

rather than

MapAt[f, slot, {}]

does yield

f[slot]

There seems to be a mismatch between what is returned by Position and what is used by MapAt, despite the documentation. Why is this? I don't really want to have to handle position {} as a special case. (The real context consists of varied nested list expressions, in general having more than one slot but also possibly consisting of just a single slot, where I want to manipulate each slot individually.)

Extract and ReplacePart also seem to behave like MapAt, expecting {{}} not {} for the whole expression.

$\endgroup$
  • $\begingroup$ Just for the record, expr /. slot -> f[slot] might be a simpler solution for your original problem, but your question is an interesting observation anyway, and I'm looking forward to answers. :) $\endgroup$ – Martin Ender Feb 2 '17 at 13:48
  • 1
    $\begingroup$ {} isn't an actual position. Position[{1, 2, 3}, 4] also returns {}. $\endgroup$ – Feyre Feb 2 '17 at 13:52
  • 1
    $\begingroup$ But Position[{1, 2, 3}, {1, 2, 3}] returns {{}} --- note the difference. $\endgroup$ – David Bevan Feb 2 '17 at 13:53
  • 1
    $\begingroup$ You can use a list of position specifications instead of a single position specification. This resolves the ambiguity. I.e. use {{}} instead of {}. {} is interpreted as an empty list of position specifications, not as a single position specification. If you use this in Extract, be aware that it will return a list of items as a result, i.e. Extract[{1, 2, 3}, {{}}] gives {{1,2,3}} (a list of a single result) instead of {1,2,3}. $\endgroup$ – Szabolcs Feb 2 '17 at 14:34
  • 2
    $\begingroup$ I agree with you that this behaviour is fairly arbitrary. The difficulty comes from having to decide whether the second argument of Extract (or related functions) is (1) an index (2) or a single position specification (3) or a list of position specifications. If we think carefully, we can identify rules through which all this makes sense. (a) a position specification is always a list (b) the second argument of Extract is interpreted as a list of position specifications whenever possible. But I don't think this will make you happy because you already know the solution. $\endgroup$ – Szabolcs Feb 2 '17 at 14:37
7
$\begingroup$

I agree that this behaviour is somewhat arbitrary. It comes from the ambiguity between indices, position specifications and lists of positions specifications. If we think carefully, we can identify the rules that Mathematica uses to resolve this ambiguity, but I am not sure that stating these rules will make you happy...

For simplicity, let us discuss only Position and Extract. MapAt and ReplacePart behave the same way.

What can be the second argument of Extract?

Extract can take:

  • an integer index
  • a position specification
  • a list of position specifications (but not a list of indices!)

Where is the ambiguity?

A position specification is always a list of integers. It can also be an empty list, {}, which refers to the entire expression.

Extract does accept simple integer indexes for convenience, but these are not considered proper position specifications.

Thus:

Extract[{{1, 2, 3}, {4, 5, 6}}, 1] (* single index *)
(* {1, 2, 3} *)

Extract[{{1, 2, 3}, {4, 5, 6}}, {1}] (* single position specification *)
(* {1, 2, 3} *)

Extract[{{1, 2, 3}, {4, 5, 6}}, {{1}}] (* a list of position specifications *)
(* {{1, 2, 3}} *)

You will notice that {} is ambiguous. It can be interpreted as either

  • an (empty) list of position specifications
  • or a single position specification (in which case it refers to the whole expression)

Extract always prefers an interpretation as a list of position specifications! Thus it interprets {} as an empty list of position specifications.

Why does it make sense for Extract to try to interpret its second argument as a list of position specifications?

Because that is what Position returns. It always, realiably returns a list. If Position retusn {}, it means that it did not find the pattern. If it returns {{}}, it means that the pattern matched the whole expression, but no subexpression of it.

Thus we can consistently use

Extract[expr, Position[expr, patt]]

This is the workaround to your problem. Do not extract the first position, or if you do, make sure to put it into a list.

Remember that when Extract receives a list of position specifications as its seond argument, it returns a list as the result.

Thus

Extract[{1, 2, 3}, {{}}]
(* {{1, 2, 3}} *)

The result is not {1,2,3} but {{1,2,3}}.

With MapAt and ReplacePart this is not an issue.

In certain cases, this behaviour generalizes well. For example, to extract the first part of expr which matches patt, you can use

Extract[expr, Take[Position[expr, patt], UpTo[1]]]

This will work even when the pattern isn't found at all. It returns a list with zero or one elements.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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