In this blog let’s look at comprehensions, you can get the notebook here https://gitlab.com/data-science-with-julia/code/-/blob/master/comprehension.ipynb. First we define a range as shown:

range = 1:10

Output:

1:10

Now say we want to square the values in the range, we can do it as shown:

[value^2 for value in range]

Output:

10-element Array{Int64,1}:
   1
   4
   9
  16
  25
  36
  49
  64
  81
 100

Look at the construct of the program, we separate out each item in the range using this statement for value in range, each time the item gets stored in a variable called value, and in value^2 we square it, and we capture it in an array by wrapping it all between square brackets like this [value^2 for value in range]. This trickery is called list comprehension.

Below let me introduce to you the rand() function, in the below example we want to generate random number between 1 and 100, so we use rand(1:100), and we want to generate 10 numbers, so we pass 10 as the second argument as ten like this rand(1:100, 10). Type the example below and execute:

array = rand(1:100, 10)

Output:

10-element Array{Int64,1}:
  70
  41
  87
  48
  54
  63
  61
 100
  26
  37

As you see from the above example, we assign the random numbers to a variable called array. Now let’s use list comprehension to take square root of array as shown:

square_root = [value^0.5 for value in array]

Output:

10-element Array{Float64,1}:
  8.366600265340756
  6.4031242374328485
  9.327379053088816
  6.928203230275509
  7.3484692283495345
  7.937253933193772
  7.810249675906654
 10.0
  5.0990195135927845
  6.082762530298219

Okay, list comprehension is very powerful feature, but one can take square of array as shown:

array.* array

Output:

10-element Array{Int64,1}:
  4900
  1681
  7569
  2304
  2916
  3969
  3721
 10000
   676
  1369

So in the above example we do element wise multiplication, for that we use the .* operator. I tried out array * array and hoped that it would work, but it did not.

The dot . means element wise operation. We can do element wise squaring of the array as shown:

array.^2

Output:

10-element Array{Int64,1}:
  4900
  1681
  7569
  2304
  2916
  3969
  3721
 10000
   676
  1369

All we do add a dot . before ^ and that’s it. So does it mean list comprehension is dead? Nope we can do some trickery with list comprehension as shown:

odd_numbers = [value for value in array if value % 2 != 0]

Output:

5-element Array{Int64,1}:
 41
 87
 63
 61
 37

Let’s see how the program works. First we take each item in the array and assign it to a variable called value here for value in array, but we do not do that all the time, it happens only when the value is odd as given by if value % 2 != 0, if that happens we take the value and put in in an Array like this [value .....]. So we have complete program as shown here [value for value in array if value % 2 != 0]. So what we are doing is filtering the odd numbers, nothing else.

Generator Comprehension

Just like list comprehension, in which things are wrapped around by square braces, we use round braces for a thing called generator comprehension. If you look at the example below, we do squaring elements of range:

squares = (el^2 for el in range)

Output:

Base.Generator{typeof(range),var"#3#4"}(var"#3#4"(), range)

but we do it inside round braces, so we get an output as shown above. That is squares are not evaluated unless absolutely necessary. So to force the evaluation we run the code below

join(squares, ", ")

Output:

"1, 4, 9, 16, 25, 36, 49, 64, 81, 100"

Where the method join(), joins the element of passed collection squares in this case with a string ", ".

Permutation

If we roll two dice, let’s see what permutations we get. Type the code below and execute:

dice_range = 1:6

# Finding permuation for roll of two dice

dice_permuations = [(x, y) for x = dice_range, y = dice_range]

Output:

6×6 Array{Tuple{Int64,Int64},2}:
 (1, 1)  (1, 2)  (1, 3)  (1, 4)  (1, 5)  (1, 6)
 (2, 1)  (2, 2)  (2, 3)  (2, 4)  (2, 5)  (2, 6)
 (3, 1)  (3, 2)  (3, 3)  (3, 4)  (3, 5)  (3, 6)
 (4, 1)  (4, 2)  (4, 3)  (4, 4)  (4, 5)  (4, 6)
 (5, 1)  (5, 2)  (5, 3)  (5, 4)  (5, 5)  (5, 6)
 (6, 1)  (6, 2)  (6, 3)  (6, 4)  (6, 5)  (6, 6)

So let’s see how the code works. So we are using list comprehension, and we are wrapping the output in square brackets [], we are wrapping a Tuple inside a list [(x, y)], and the Tuple is fed by variables x and y. These x and y take their values in an iterative manner from dice_range that ranges from 1 to 6 here for x = dice_range, y = dice_range. So this concise piece of code generates us a 6X6 array with all permutations of xand y. Below we check the size of the permutation as though we don’t trust Julia:

size(dice_permuations)

Output:

(6, 6)

Flattened Comprehension

The comprehension discussed above returned a rank 2 matrix, but what if want to generate a flattened one? See the example below:

flattened_permuations = [(x, y) for x = dice_range for y = dice_range]

Output:

36-element Array{Tuple{Int64,Int64},1}:
 (1, 1)
 (1, 2)
 (1, 3)
 (1, 4)
 (1, 5)
 (1, 6)
 (2, 1)
 (2, 2)
 (2, 3)
 (2, 4)
 (2, 5)
 (2, 6)
 (3, 1)
 ⋮
 (5, 1)
 (5, 2)
 (5, 3)
 (5, 4)
 (5, 5)
 (5, 6)
 (6, 1)
 (6, 2)
 (6, 3)
 (6, 4)
 (6, 5)
 (6, 6)

In the above example we simply use 2 for’s like this for x = dice_range for y = dice_range. This generates us a vector of Tuple’s rather than a matrix as in the previous example. Now let’s check its size to conform it’s a vector:

size(flattened_permuations)

Output:

(36,)