22
$\begingroup$

I am confused by why Mathematica uses [[3]] to get the 3rd element, or [[i,j] to get the i,j-th element of a 2D array.

This seems counter-intuitive. Is this main reason for this to separate array-indexing from function calls?

i.e. where I see:

f[3, 4] <-- this is currently function call
f[[3, 4]] <-- f is a 2D array, and we're accessing 3,4-th element

Question:

Given that Mathematica knows the type of the arguments, can't it infer:

  • if we're dealing with a function, apply arguments
  • if the object is an array, index it?

    Thus, why do we need separate [[ ]] notation for array indexing?

$\endgroup$
1
  • 13
    $\begingroup$ most programs are written to also be read by other humans, in which case the distinction between [[ ]] and [ ] is helpful. $\endgroup$
    – rm -rf
    Jul 10, 2012 at 4:04

3 Answers 3

26
$\begingroup$

In addition to Brett's counter-example, it might be helpful to view this from Mathematica's philosophy, which is "everything is an expression". In this framework, you're not really indexing a 1D/2D array, but you're extracting a Part from an expression.

Indeed, you can use the ⟦ ⟧ notation on any expression, not just lists/matrices. For example:

Sin[x + y][[1]]
(* x + y *)

Graphics[{Red, Disk[]}][[1, 1]]
(* RGBColor[1, 0, 0] *)

The output of Part on any expression can be viewed as the argument of some function in the FullForm of the expression. For example, breaking down the second example above:

Graphics[{Red, Disk[]}][[1]]
(* {RGBColor[1, 0, 0], Disk[{0, 0}]} <-- argument of Graphics[] *) 

Graphics[{Red, Disk[]}][[1, 1]]
(* RGBColor[1, 0, 0] <-- argument of List[] *)

Graphics[{Red, Disk[]}][[1, 2, 1]]
(* {0, 0} <-- argument of Disk[] *)

The 0th part is the Head of the entire expression. Since none of the above constitute as being a function call, it makes sense to use a different notation to avoid any ambiguity.

$\endgroup$
0
18
$\begingroup$

No.

For example, functions do not have to be atomic. It can be possible to extract parts from them (although it's generally not recommended.)

In[1]:= if=Interpolation[Range[10]^2]

Out[1]= InterpolatingFunction[{{1,10}},<>]

In[2]:= if[3]

Out[2]= 9

In[3]:= if[[3]]

Out[3]= {{1,2,3,4,5,6,7,8,9,10}}
$\endgroup$
9
$\begingroup$

Let me give two examples illustrating the conceptual difference between Part and [].

Here's an example showing how parts of every expression, even graphics expressions, can be accessed using Part:

g = Graphics[Circle[]];
g
g // FullForm

so g is, in fact, Graphics[Circle[List[0, 0]]], even though Mathematica displays it in the front-end as

Mathematica graphics

So you can extract its parts directly: g[[1, 1, 2]] evaluates to 0, for example.

So Part is a way of accessing specific parts on an expression tree.

On the other hand, you can think of f[2] as analogous to a hashtable. Thus,

Do[f[i] = RandomReal[], {i, 3}]
DownValues[f]
(*
{HoldPattern[f[1]] :> 0.201718, 
 HoldPattern[f[2]] :> 0.287401, 
 HoldPattern[f[3]] :> 0.531829}
*)

and eg f[2] evaluates to 0.287401. Defining this kind of thing with patterns, like f[x_]:=x^2 is no different: you are merely matching a more general pattern than you are with eg f[3]. You could have arbitrary expressions inside the brackets, and this is often used to implement memoization, although it needs some work for more complicated situations.

So actually f[[3]] and f[3] are conceptually completely different operations.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.