In some of the previous modules, we have used a notation that looked like
`function.(arguments)`

, or `x .+ y`

. In this module, we will talk about what
the `.`

notation does, and more broadly, what *broadcasting* is.

Let’s generate two arrays:

```
x, y = [1, 2, 0], [4, 2, 1]
```

```
([1, 2, 0], [4, 2, 1])
```

We think of them as arrays, but in practice, they are *column vectors*, and
therefore have their own arithmetic. For example, we can add them
(element-wise):

```
x + y
```

```
3-element Vector{Int64}:
5
4
1
```

But the multiplication is not defined. If we want to do an element-wise multiplication, we need to specify that we are applying an operation to each elements:

```
x .* y
```

```
3-element Vector{Int64}:
4
4
0
```

We can also take the difference between these vectors (element-wise):

```
x - y
```

```
3-element Vector{Int64}:
-3
0
-1
```

But diving one by the other can give an unexpected result:

```
x / y
```

```
3×3 Matrix{Float64}:
0.190476 0.0952381 0.047619
0.380952 0.190476 0.0952381
0.0 0.0 0.0
```

What is going on? In simple terms, `/`

is defined as the multiplication of its
first argument by the inverse of its second, and because *Julia* will adhere
very strictly to what symbols means mathematically, this operation works.

This behavior can be surprising when coming from other languages, but this is
a fact of life. If we want to *ensure* we work in an element-wise way, we need
to use the `.`

notation:

```
x ./ y
```

```
3-element Vector{Float64}:
0.25
1.0
0.0
```

This (using `.`

to work on the elements instead of the entire object) is a
nice little bit of syntactic sugar around what the operation actually is:

```
broadcast(*, x, y)
```

```
3-element Vector{Int64}:
4
4
0
```

In fact, we can call the `.*`

version with the `@lower`

macro from Meta,
and see that it is indeed performing broadcasting:

```
Meta.@lower x .* y
```

```
:($(Expr(:thunk, CodeInfo(
@ none within `top-level scope`
1 ─ %1 = Base.broadcasted(*, x, y)
│ %2 = Base.materialize(%1)
└── return %2
))))
```

Using element-wise notation can be extremely important when dealing with higher dimensional objects. For example, if we have two matrices:

```
U = [1 0; 0 1]
V = [1 2; 1 3]
```

```
2×2 Matrix{Int64}:
1 2
1 3
```

Calling `*`

will perform *matrix multiplication*, because the `*`

operator has
a well accepted behavior when its arguments are matrices:

```
U * V
```

```
2×2 Matrix{Int64}:
1 2
1 3
```

In order to perform the Hadamard product, we need to call `.*`

instead:

```
U .* V
```

```
2×2 Matrix{Int64}:
1 0
0 3
```

Again, this is equivalent to

```
broadcast(*, U, V)
```

```
2×2 Matrix{Int64}:
1 0
0 3
```

The `.`

can also be used *betwen* a function and its arguments. For example,
we can check for the odd elements in a matrix:

```
isodd.(V)
```

```
2×2 BitMatrix:
1 0
1 1
```

Which is again identical to

```
broadcast(isodd, V)
```

```
2×2 BitMatrix:
1 0
1 1
```

There is an interesting bit of notation we can use, which is to prefix a line
where we *only* do element-wise operations with `@.`

:

```
@. (x - y) / (x + y)
```

```
3-element Vector{Float64}:
-0.6
0.0
-1.0
```

This is equivalent to the more lengthy form using a `.`

before each operator:

```
(x .- y) ./ (x .+ y)
```

```
3-element Vector{Float64}:
-0.6
0.0
-1.0
```

Using this notation will often be a good way to avoid writing a loop, and to apply a function rapidly to any collection. Named tuples and dictionaries are still going to resist this syntax, for reasons that are not necessarilly worth explaining here; but we have seen in previous modules how we can iterate over them regardless.