0 — Julia for Python users

A syntax bridge before the performance story

Emmanuel Pilliat — ENSAI 3A

Goals

  • Dynamic, REPL-driven, like Python: only what differs.
  • No performance, no parallelism yet.

Four traps: arrays start at 1, blocks close with end, the dot, functions not classes.

▶ Script: 0_julia_basics.jl · Notebook: 0_julia_basics.jl

⬅ Previous: Overview · Next: 1 — Interpreted vs compiled ➡

Values

Variables & types

Assign with =, no declaration.

x = 3            # Int64
y = 2.5          # Float64
name = "Ada"     # String

typeof(x) = Python’s type(x).

Numbers

10 / 4      # 2.5  (true division)
10 ÷ 4      # 2    (Python's //)
10 % 4      # 2
2^10        # 1024
sqrt(2)     # 1.414…
  • Power is ^. Bet first: what does 2**10 do?
  • How do you type ÷? Next slide.

▶ Script: cell “Numbers” — try 2**10.

Unicode: \name then TAB

10 ÷ 4               # \div
α = 0.1              # \alpha
3 ≤ 4                # \le
2 ∈ [1, 2, 3]        # \in
2π                   # 2 * π

2α is 2 * α: formulas map straight to code.

▶ Script: cell “Unicode & juxtaposition”.

Strings

a, b = 3, 4
"Hello, $name"          # "Hello, Ada"
"$a + $b = $(a + b)"    # "3 + 4 = 7"
"foo" * "bar"           # "foobar"
'a'                     # a Char
  • $ = f-string. Bet first: "foo" + "bar"? Concatenation is *.
  • "…" text; '…' one character.

▶ Script: cell “Strings”.

Collections

Arrays start at 1

v = [10, 20, 30, 40, 50]
v[1]        # 10  (v[0]: error)
v[end]      # 50
v[end-1]    # 40
v[2:4]      # [20, 30, 40]
length(v)   # 5
Python Julia
First v[0] v[1]
Last v[-1] v[end]
Slice 2:4 2, 3 2, 3, 4

▶ Script: cell “Arrays: 1-based” — try v[0].

Ranges, comprehensions, matrices

1:5                             # 1, 2, 3, 4, 5
1:2:9                           # 1, 3, 5, 7, 9
[i^2 for i in 1:5]              # [1, 4, 9, 16, 25]
[i for i in 1:10 if iseven(i)]  # [2, 4, 6, 8, 10]
M = [1 2 3; 4 5 6]              # 2×3 matrix
M[2, 3]                         # 6
  • Ranges are lazy; collect(1:2:9) builds the array.
  • Matrices are column-major (opposite of NumPy).

Tuples, named tuples, dicts

t = (1, "two", 3.0)
p, q, s = t                  # q == "two"
t[1]                         # 1

nt = (x = 10, y = 20)
nt.x                         # 10

d = Dict("apple" => 3, "pear" => 5)
d["apple"]                   # 3
haskey(d, "pear")            # "pear" in d

Control flow

Blocks close with end

No :, no indentation rule.

for i in 1:3
    println(i)
end            # ← closes the loop
  • for, while, if, function, struct: each closed by end.
  • Indentation: for readers only.

Conditionals

x = 11
if x ≥ 16
    "excellent"
elseif x ≥ 10
    "pass"
else
    "fail"
end                    # "pass"

x ≥ 0 ? "pos" : "neg"  # ternary

elseif, one word. c ? a : b = Python’s a if c else b.

Loops

words = ["a", "b"]
for (i, w) in enumerate(words)
    println(i, " ", w)
end

[(i, w) for (i, w) in enumerate(words)]  # [(1, "a"), (2, "b")]
  • for … end, comprehensions: as in Python.
  • enumerate counts from 1.

let … end: a local scope

v = [10, 20, 30, 40, 50]
let
    acc = zero(eltype(v))      # 0 of v's type
    for i in eachindex(v)      # 1:5
        acc += v[i]
    end
    acc                        # → 150
end

Like a function body run at once. Later scripts time code inside let.

Functions & types

Functions — three forms

function area_rect(L, w)      # 1) long
    return L * w
end

square(x) = x^2               # 2) one-line
cube = x -> x^3               # 3) lambda

greet(who; punct = "!") = "Hi $who$punct"   # kwarg after ;
greet("Bob"; punct = "?")                    # "Hi Bob?"
  • return optional: last expression is returned.
  • Trailing ! (push!, sort!): modifies its argument (convention).

Broadcasting: the dot .

xs = [1.0, 4.0, 9.0]
sqrt.(xs)       # [1.0, 2.0, 3.0]
xs .+ 1         # [2.0, 5.0, 10.0]
@. xs^2 + 1     # dot on EVERY op
  • Bet first: sqrt(xs), without the dot?
  • Works on your functions: square.(xs).
  • Chained dots fuse into one loop.

▶ Script: cell “Broadcasting” — try sqrt(xs).

Structs, not classes

struct Point            # immutable
    x::Float64
    y::Float64
end

mutable struct Counter; n::Int; end

pt = Point(1.0, 2.0)    # no __init__
c = Counter(0); c.n += 1  # fine
# pt.x = 5.0            # ERROR
  • No self: behavior in functions (dispatch module).

▶ Script: cell “Structs instead of classes” — try pt.x = 5.0.

Packages

using Statistics           # stdlib: just load

using Pkg
Pkg.add("BenchmarkTools")  # once (pip install)
using BenchmarkTools       # import

using ≈ from numpy import *.

▶ Script: cell “Packages”.

Monte-Carlo π

Monte-Carlo π — the running example

function count_in_disk(n)
    hits = 0
    for _ in 1:n
        x, y = rand(), rand()
        if x^2 + y^2 ≤ 1
            hits += 1
        end
    end
    return hits
end
n = 100_000
4 * count_in_disk(n) / n       # ≈ 3.14

Back in every module: timed, then threads, then GPU. ▶ Script: cell “5. Monte-Carlo π”.

Cheat-sheet — Python → Julia

Topic Python Julia
First / last v[0] / v[-1] v[1] / v[end]
Slice v[1:4] excl. 4 v[2:4] incl. 4
Blocks / else-if : + indent / elif end / elseif
Power / int-div 2**10 / // 2^10 / ÷
Concat / f-string "a"+"b" / f"{x}" "a"*"b" / "$x"
Element-wise np.f(x) f.(x)
Method call obj.method() method(obj)
Class class C + self struct C
Copy vs alias b = a[:] copies b = a aliases; copy(a)
Absent value None nothing / missing

Going further

⬅ Previous: Overview · Next: 1 — Interpreted vs compiled ➡