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.
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{}
isn't an actual position.Position[{1, 2, 3}, 4]
also returns{}
. $\endgroup$ – Feyre Feb 2 '17 at 13:52Position[{1, 2, 3}, {1, 2, 3}]
returns{{}}
--- note the difference. $\endgroup$ – David Bevan Feb 2 '17 at 13:53{{}}
instead of{}
.{}
is interpreted as an empty list of position specifications, not as a single position specification. If you use this inExtract
, 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:34Extract
(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 ofExtract
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