Chapter 2: Variables

We have, of course, encountered variables in the previous chapter, where we used some for string substitution. For a functional programming language, mastery of how variables work is essential. Julia is extremely flexible when it comes to variables, but wielding this flexibility will require finesse.

Assigning variables

To assign a variable, simply use = (a single equals sign). That, really, is it. Nice, huh?

    julia> m = 2.32
    2.32 

    julia> m
    2.32

Accessing an undefined variable yields an exception, of course:

    julia> n
    ERROR: n not defined

Julia does not require you to explicitly declare variables before assignment (indeed, there is no explicit way to do so).

Variable naming

In general, you can use just about anything you want as a variable name. This includes Unicode characters from a quite astounding range. In case you ever wanted to give your code some Christmas spirit, you are free to do so. In the following, we will be proving, using the comparison operator (>), that winter is not warmer than summer:

    julia> ❄ = -12
    -12

    julia> ✹ = 27
    27

    julia> ❄ > ✹
    false

The above is, believe it or not, perfectly valid Julia. Whether it is perfectly sensible Julia, too, is a different question. In general, using Unicode variable names for anything but commonly accepted scientific symbols, such as ћ, is not the best idea, lest you will leave readers of your code trying to figure out meanings of the thirty or so similar looking Unicode symbols.

The stylistic convention of Julia is to use lowercase variable names, and with words separated by _ (underscore) only if necessary for the sake of legibility. Variable names may not start with a number nor an exclamation mark.

Goodwin's dream

In 1894, an amateur mathematician from Indiana, Edward J. Goodwin, proposed a way to square the circle (a feat proven to be impossible by the Lindeman-Weierstrass theorem proven a decade or so earlier) [wikipedia]. He lobbied long enough for his 'new mathematical truth' to be introduced as a Bill in the Indiana House of Representatives, in what became known as the Indiana Pi Bill of 1897. This inferred a value of approximately 3.2 for π. Fortunately, the intervention of Purdue professor C.A. Waldo helped to defeat the already much-ridiculed bill in the Senate, and the matter was laid to rest.

Julia, on the other hand, allows you to redefine the value of π. The following is entirely correct Julia:

    julia> π
    π = 3.1415926535897...

    julia> π = 3.2
    Warning: imported binding for π overwritten in module Main
    3.2

    julia> π
    3.2

It is, on the other hand, really bad practice to redefine set constants. Therefore, should you encounter the opportunity to redefine π, learn from the sad case of Mr. Goodwin and try to resist the temptation.

Assigning variables to variables

It is important to understand what the effect of assigning a variable to a variable is. It is perfectly valid Julia to have multiple variables point at each other. However, doing so creates a 'shallow copy' – that is, a reference to the same memory address of where the original copy is located. If you modify one variable pointing at it, the value of the other changes, too. In the following example, we will be creating an array m, then set n be equal to m. We then carry out an operation that changes the value of m, marked by the exclamation mark (don't worry if that part is new to you, it will be explained in Exercise 3: Collections – for now, all you need to know is that pop! takes the last element of an indexable collection, returns it and removes it from the collection). When we then call n, we see that its value, too, has been affected by the operation – that is because it did not so much have a value but acted merely as a reference to 'whatever is in m'.

    julia> m = [1,2,3,4]        # Creating array
    4-element Array{Int64,1}:
     1
     2
     3
     4

    julia> n = m                # Setting n to point to m
    4-element Array{Int64,1}:
     1
     2
     3
     4

    julia> pop!(m)                # Altering m
    4

    julia> n                    # n is also changed as a result.
    3-element Array{Int64,1}:
     1
     2
     3

We will be considering a way to get around this by 'deep copying' a value in chapter [X].

Literals

Literals are ways to enter various data types, whether into the REPL or a program.

Strings

We have already encountered string literals. To enter a string literal, simply delimit it by " (double quotation marks). Unlike Python or JavaScript, Julia does not accept single quotation marks. See [Chapter 5] for more information.

One-dimensional arrays

1D array literals are delimited by square brackets ([]). Each element they contain has to be either a variable or an otherwise valid literal, but they do not all have to be the same type (something true for most collections within Julia). Values are separated by a , (comma):

    julia> arr1 = [1, 2, "sausage", π]
    4-element Array{Any,1}:
         1
         2
          "sausage"
         π = 3.1415926535897...

Ranges

A range, in Julia, is simply a shorthand for a sequence of numbers that are all spaced equally. A range can be created using the range() function as range(start, end), but it is usually denoted in a shorthand literal, start:end. Ranges are lazy, meaning that the values are not immediately available until they are needed. You can, however, force the generation of the values by using the collect() function.

    julia> 0:5
    0:5

    julia> typeof(0:5)
    UnitRange{Int64}

    julia> collect(0:5)
    6-element Array{Int64,1}:
     0
     1
     2
     3
     4
     5

As you can see, a range in Julia includes both its start and end element. A range doesn't have to be created between integers – [0.5:3.5] would return an array of [0.5, 1.5, 2.5, 3.5].

A range may have an optional middle step attribute. To obtain an array of the numbers from 0 to 30 in steps of 10:

    julia> collect(0:10:30)
    4-element Array{Int64,1}:
      0
     10
     20
     30

Because range is largely implemented as [Collections], you can use most of the convenience functions:

    julia> for i in 1:3 println("$i great") end
    1 great
    2 great
    3 great

    julia> 2 in 1:10
    true

    julia> length(1:10)
    10

    julia> sum(1:10)
    55

    julia> reduce(*, 1:10)
    3628800

Multidimensional arrays

In some languages, multidimensional arrays can be created by enclosing one-dimensional arrays in another array, and so on. Unfortunately, that is not the case in Julia, so [[1,2],[3,4]] would yield the one-dimensional array [1,2,3,4]. Rather, to create a multidimensional array, separate values by empty space and rows by a ; (semicolon):

    julia> md_array = [1 1 2 3 5; 8 13 21 34 55; 89 144 233 377 610]
    3x5 Array{Int64,2}:
      1    1    2    3    5
      8   13   21   34   55
     89  144  233  377  610

Alternatively, you can enter columns, using square brackets (remember not to let your ingrained reflexes from Python take over and put a comma between the arrays!):

    julia> [[1,2,3] [4,5,6]]
    3x2 Array{Int64,2}:
     1  4
     2  5
     3  6

When entering a multidimensional array, each row has to have the same length. Failing to do so raises an error:

    julia> md_sparse_array = [1 1 2; 8 13 21 34 55]
    ERROR: argument count does not match specified shape
     in hvcat at abstractarray.jl:802

Tuples

Tuples are similar to one-dimensional arrays, in that they consist of a number of values. They are, however, unlike array literals in that they are immutable – once assigned, you cannot change the values in a tuple. Tuples are delimited by () (round brackets) and values are separated by commas.

    julia> fibo_tuple = (1, 1, 2, 3, 5)
    (1,1,2,3,5)

To demonstrate the difference between arrays and tuples, consider the following:

    julia> fibo_arr = [1, 1, 2, 3, 5]
    5-element Array{Int64,1}:
     1
     1
     2
     3
     5

    julia> fibo_arr[2] = 0
    0

    julia> fibo_arr
    5-element Array{Int64,1}:
     1
     0
     2
     3
     5

    julia> fibo_tuple[2] = 0
    ERROR: `setindex!` has no method matching setindex!(::(Int64,Int64,Int64,Int64,Int64), ::Int64, ::Int64)

In this listing, we have created an array with the first five non-zero elements of the Fibonacci sequence. We then have used an accessor (fibo_arr[2] is an accessor that retrieves the second element of the array fibo_arr) to change the second value in the array to zero. Calling the array shows that this was successful. On the other hand, trying to do the same with the tuple fibo_tuple of the same values that we declared earlier yields an error. This is because tuples are immutable.

Dicts

Dicts are what in some other languages are known as associative arrays or maps: they contain key-value pairs. A dict is delimited by round brackets. Individual mappings (key-value pairs) are separated by commas. Keys are separated from values by => (a double-arrow).

    julia> statisticians = Dict("Gosset" => "1876-1937", "Pearson" => "1857-1936", "Galton" => "1822-1911")
    Dict{String,String} with 3 entries:
      "Galton"  => "1822-1911"
      "Pearson" => "1857-1936"
      "Gosset"  => "1876-1937"

Sets

Sets are similar to other collections in that they contain various values. However, unlike arrays and tuples, they are unordered and unique-constrained: no element may occur multiple times within the set.

Sets do not have a specific notation, but rather are created using a syntax we will use a great deal for creating all kinds of objects: by using the type (Set) and entering the values to constitute the set in round brackets and square braces:

    julia> stooges = Set(["Moe", "Curly", "Larry"])
    Set(String["Moe","Larry","Curly"])

Sets do accept duplicates at time of construction, but the resulting set will still only contain one of each unique element:

    julia> beatles = Set(["Lennon", "McCartney", "Harrison", "Starr", "Lennon"])
    Set(String["Starr","Harrison","McCartney","Lennon"])

Sets are unordered, meaning that two sets containing the same elements are equal:

    julia> Set(["Marsellus", "Jules", "Vincent"]) == Set(["Jules", "Vincent", "Marsellus"])
    true

An empty set is created by

    julia> Set([])
    Set{Any}()

Conclusion

In this chapter, we have learned how to construct a handful of literals for the main data types and assign them to variables. This should give us a promising start for our next chapter, in which we are going to explore collections.

results matching ""

    No results matching ""