Sidef

July 2, 2026 · View on GitHub

Intermediate and advanced techniques for experienced Sidef programmers

Official DocumentationGitHubExample ScriptsTry Online


Prerequisites: This guide assumes you already have Sidef installed and working, and that you are comfortable with the fundamentals — variables, control flow, basic functions, simple arrays and hashes, and basic OOP. If you need a refresher on those, see the Beginner's Guide first.


Table of Contents

  1. Command-Line Flags and the REPL
  2. Special Quote Operators and Strings
  3. Advanced Functions
  4. Metaoperators
  5. Sets, Bags, and Pairs
  6. Ranges in Depth
  7. Lazy Evaluation, gather/take, and Enumerators
  8. Advanced OOP
  9. Special Numeric Types
  10. Number Theory
  11. Arbitrary Precision and Floating-Point
  12. Functional Programming Patterns
  13. Regular Expressions
  14. File and Directory I/O
  15. Perl Module Integration
  16. Sorting Algorithms
  17. Dynamic Programming and Memoization
  18. Matrix and Vector Arithmetic
  19. Encoding, Compression, and Cryptography Patterns
  20. Putting It All Together: Larger Examples
  21. Further Resources

1. Command-Line Flags and the REPL

Beyond just running sidef script.sf, there are several useful flags worth knowing.

# Run a one-liner
sidef -e 'say 100.primes'

# Set floating-point precision to 50 decimal places
sidef -P50 -e 'say Num.pi'

# Print the parsed representation of a script (useful for debugging precedence)
sidef -r script.sf

# Compile to Perl (advanced: inspect generated Perl code)
sidef -Rperl script.sf
sidef -Rperl script.sf | perltidy

# Profile a Sidef script with Devel::NYTProf
sidef -Rperl script.sf | perl -d:NYTProf && nytprofhtml --open -m

In the REPL, you can inspect any value just by typing it — no say needed:

$ sidef
> 2**100
1267650600228229401496703205376
> 100.factorial.len
158
> "hello".chars.reverse.join
olleh

2. Special Quote Operators and Strings

Sidef has a rich set of quoting mechanisms beyond plain strings.

Word Arrays

var fruits = %w(apple banana cherry)
# Equivalent to: ["apple", "banana", "cherry"]

var paths = <usr local bin>
# Also a word array: ["usr", "local", "bin"]

Heredocs

var poem = <<'EOF'
Roses are red,
Violets are blue.
No interpolation here: #{1+2}
EOF

var greeting = <<-"EOT"
    Hello, #{name}!
    Today is a great day.
    EOT

The <<- form strips leading whitespace so the closing delimiter can be indented naturally.

Shell Execution

var ls_output = %x(ls -la)
var files     = %x(find . -name "*.sf").lines

Symbol Literals

Symbols are just single-quoted strings with a compact notation — useful as hash keys:

var h = Hash(:name => "Alice", :age => 30)
say h{:name}    # Alice

String Interpolation Tricks

Any expression can be interpolated inside #{}:

var n = 12
say "The #{n}th prime is #{n.prime}"    # The 12th prime is 37
say "Sum 1..100 = #{(1..100).sum}"      # Sum 1..100 = 5050

Multi-line method chaining with backslash continuation:

say "hello world" \
    .split(' ')   \
    .map { .tc }  \
    .join(' ')
# => Hello World

3. Advanced Functions

Default and Named Parameters

func greet(name = "World", punct = "!") {
    say "Hello, #{name}#{punct}"
}

greet()                        # Hello, World!
greet("Alice")                 # Hello, Alice!
greet(name: "Bob", punct: ".") # Hello, Bob.
greet(punct: "?")              # Hello, World?

Variadic Functions

func sum(*nums) {
    nums.reduce('+', 0)
}
say sum(1, 2, 3, 4, 5)    # 15

func log_all(String prefix, *msgs) {
    msgs.each { |m| say "#{prefix}: #{m}" }
}
log_all("INFO", "started", "running", "done")

Return Type Constraints

func square(Number n) -> Number {
    n**2
}

func greet(String name) -> String {
    "Hello, #{name}!"
}

Multiple Dispatch

Sidef resolves calls to the most specific matching overload:

func describe(String s)  { say "String:  #{s}" }
func describe(Number n)  { say "Number:  #{n}" }
func describe(Array  a)  { say "Array:   #{a}" }

describe("hi")    # String:  hi
describe(42)      # Number:  42
describe([1,2,3]) # Array:   [1, 2, 3]

Pattern Matching in Function Arguments

Match on literal values using double parentheses:

func fib ((0)) { 0 }
func fib ((1)) { 1 }
func fib  (n)  { fib(n-1) + fib(n-2) }

say fib(10)    # 55

Match on value predicates (block guards):

func sign(Number n { _ < 0 })  { -1 }
func sign(Number n { _ == 0 }) {  0 }
func sign(Number n { _ > 0 })  {  1 }

say sign(-7)    # -1
say sign(0)     #  0
say sign(5)     #  1

Closures and Higher-Order Functions

func make_adder(n) {
    func(x) { x + n }
}

var add10 = make_adder(10)
var add42 = make_adder(42)

say add10(5)     # 15
say add42(100)   # 142
say [1,2,3].map(add10)    # [11, 12, 13]

Closures capture their environment by reference, so they can act as stateful objects:

func make_counter(start = 0) {
    var n = start
    Hash(
        inc  => func { ++n },
        dec  => func { --n },
        get  => func {   n },
        reset => func { n = start },
    )
}

var c = make_counter(10)
c{:inc}()
c{:inc}()
c{:inc}()
say c{:get}()    # 13
c{:reset}()
say c{:get}()    # 10

Anonymous Self-Reference with __FUNC__

func fib(n) {
    n < 2 ? n : (__FUNC__(n-1) + __FUNC__(n-2))
}

This is especially useful in lambdas that need to recurse without a name:

var factorial = func(n) {
    n <= 1 ? 1 : (n * __FUNC__(n-1))
}
say factorial(10)    # 3628800

Lazy Partial Application

Turn any method into a reusable function via .method(name):

var double = 2.method('*')
say [1, 2, 3, 4].map(double)    # [2, 4, 6, 8]

var inc = 1.method('+')
say (1..5 -> map(inc))          # [2, 3, 4, 5, 6]

4. Metaoperators

Sidef has powerful array metaoperators that eliminate most explicit loops.

Element-wise (Unroll): »OP«

Apply an operator between two arrays element by element:

[1,2,3] »+«  [10,20,30]          # [11, 22, 33]
[4,9,16] »**« [0.5, 0.5, 0.5]    # [2, 3, 4]  (sqrt)

Map: »OP» and «OP«

Apply an operator or method to every element:

[1,2,3,4] »*» 10    # [10, 20, 30, 40]
[1,2,3,4] «*« 10    # [10, 20, 30, 40]

["hello","world"] >>uc()>>    # ["HELLO", "WORLD"]
[1,4,9,16] >>sqrt()>>         # [1, 2, 3, 4]

Reduce: «OP»

Fold an array with an operator:

[1,2,3,4,5]«+»     # 15
[1,2,3,4,5]«*»     # 120
[3,1,4,1,5]«max»   # 5

Cross Product: ~X

[1,2] ~X   [3,4]     # [[1,3],[1,4],[2,3],[2,4]]
[1,2] ~X+  [3,4]     # [4, 5, 5, 6]   (cross with +)
[1,2] ~X*  [3,4]     # [3, 4, 6, 8]   (cross with *)

Zip: ~Z

[1,2,3] ~Z  [4,5,6]    # [[1,4],[2,5],[3,6]]
[1,2,3] ~Z+ [4,5,6]    # [5, 7, 9]

Practical Example

Dot product of two vectors without any explicit loop:

func dot(a, b) {
    (a »*« b)«+»
}

say dot([1,2,3], [4,5,6])    # 32  (= 1*4 + 2*5 + 3*6)

5. Sets, Bags, and Pairs

Sets

Sets hold unique elements and support standard set operations:

var evens  = Set(2, 4, 6, 8, 10)
var primes = Set(2, 3, 5, 7, 11)

say (evens & primes)    # Set(2)          — intersection
say (evens | primes)    # Set(2,3,4,5,6,7,8,10,11) — union
say (evens - primes)    # Set(4,6,8,10)   — difference
say (evens ^ primes)    # symmetric difference

say evens.has(4)        # true
say evens.len           # 5

Convert back to sorted array:

say (evens | primes -> to_a.sort)   # [2, 3, 4, 5, 6, 7, 8, 10, 11]

Bags (Multisets)

A Bag is like a Set but tracks how many times each element appears:

var letters = Bag("a", "b", "a", "c", "b", "a")

say letters.count("a")    # 3
say letters.count("b")    # 2
say letters.keys.sort     # ["a", "b", "c"]

Bags are useful for frequency analysis:

var words = "the cat sat on the mat the cat".split(' ')
var freq  = Bag(words...)

freq.keys.sort_by { freq.count(_) }.reverse.each { |w|
    say "#{w}: #{freq.count(w)}"
}
# the: 3
# cat: 2
# sat: 1
# on:  1
# mat: 1

Pairs

A Pair is a lightweight two-element tuple:

var p = Pair("key", "value")
say p.first     # key
say p.second    # value

6. Ranges in Depth

Arithmetic on Ranges

Ranges support arithmetic operators that produce new ranges:

(1..10) + 5     # 6..15
(1..10) * 2     # 2..20
(2..20).by(2)   # even numbers: 2,4,6,...,20

Custom Step with by

for x in ((0..1).by(0.1)) {
    print "#{x} "
}
# 0 0.1 0.2 ... 1

for x in ((10 ^.. 1).by(3)) {
    print "#{x} "
}
# 9 6 3

upto / downto / by Chaining

(-2 `upto` 2 `by` 0.5).each { |x|
    say x
}

Range Methods

(1..100).sum                # 5050
(1..10).prod                # 3628800  (10!)
(1..20).grep { .is_prime }  # [2,3,5,7,11,13,17,19]
(1..Inf).lazy.grep { .is_prime }.first(10)  # first 10 primes

7. Lazy Evaluation, gather/take, and Enumerators

Lazy evaluation is one of Sidef's most powerful features for handling large or infinite sequences without memory overhead.

The .lazy Chain

# First 10 numbers that are both a perfect square and have digit sum 10
var result = (1..Inf).lazy \
    .map { _**2 } \
    .grep { .digits.sum == 10 } \
    .first(5)

say result    # [64, 361, 1225, 2116, 3025]

gather/take

gather runs a block and collects every value passed to take:

# Collect twin prime pairs up to 100
var twins = gather {
    for p in (primes(3, 100)) {
        take([p, p+2]) if ((p+2).is_prime)
    }
}
say twins
# [[3,5],[5,7],[11,13],[17,19],[29,31],[41,43],[59,61],[71,73]]

gather/take can also build trees or nested structures:

# Pascal's triangle rows
var pascal = gather {
    var row = [1]
    10.times {
        take(row.clone)
        row = [1, (row ~Z+ row.slice(1))..., 1]
    }
}
pascal.each { |row| say row.join(" ") }

Enumerators (Custom Lazy Sequences)

Enumerator lets you define your own infinite (or finite) lazy generators:

# Fibonacci sequence
var fibs = Enumerator({ |yield|
    var (a, b) = (0, 1)
    loop {
        yield(a)
        (a, b) = (b, a+b)
    }
})

say fibs.first(10)    # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
say fibs.nth(51)      # 12586269025  (1-indexed)

Enumerators supports various methods with conditional blocks, such as:

say fibs.first(8, { .is_prime })  # [2, 3, 5, 13, 89, 233, 1597, 28657]
say fibs.nth(5, { .is_even })     # 144
say fibs.while { _ <= 1000 }      # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]

Collatz sequence as an enumerator:

func collatz_seq(n) {
    Enumerator({ |yield|
        while (n != 1) {
            yield(n)
            n = (n.is_even ? (n>>1) : (3*n + 1))
        }
        yield(1)
    })
}

say collatz_seq(27).to_a.len    # steps to reach 1: 112

8. Advanced OOP

The init Method

init runs automatically after object construction and is useful for derived attribute setup:

class Circle(Number radius) {
    has area
    has circumference

    method init {
        area          = (Num.pi * radius**2)
        circumference = (2 * Num.pi * radius)
    }

    method scale(Number factor) {
        Circle(radius * factor)
    }
}

var c = Circle(5)
say c.area.round(-4)                    # 78.5398
say c.scale(2).circumference.round(-4)  # 62.8319

Method Overriding and super

class Shape {
    method area { 0 }
    method describe { "Shape with area #{self.area.round(2)}" }
}

class Rectangle(Number w, Number h) < Shape {
    method area { w * h }
}

class Square(Number side) < Rectangle {
    method init {
        # a square is a rectangle with equal sides
    }

    method area { side**2 }
}

var s = Square(5)
say s.describe    # Shape with area 25

Subsets for Type Refinement

Subsets act as refined types and can be used in function signatures, providing automatic validation:

subset EvenInt < Number { |n| n.is_int && n.is_even }
subset OddInt  < Number { |n| n.is_int && n.is_odd  }
subset PosNum  < Number { |n| n > 0 }

func half(EvenInt n)        { n / 2 }
func next_odd(OddInt n)     { n + 2 }
func log_safe(PosNum x)     { x.log }

say half(8)           # 4
say next_odd(7)       # 9
say log_safe(Num.e)   # 1

Mixins with Modules

Modules are first-class namespaces that can be used as mixins:

module Printable {
    method print_info {
        say "#{self.class}: #{self}"
    }
}

module Serializable {
    method to_string {
        self.class + "(" + self.to_a.join(", ") + ")"
    }
}

class Point(Number x, Number y) {
    include Printable
    include Serializable

    method to_a { [x, y] }
    method to_s { "(#{x}, #{y})" }
}

var p = Point(3, 4)
p.print_info        # Point: (3, 4)
say p.to_string     # Point(3, 4)

Operator Overloading

class Vector2D(Number x, Number y) {

    method +(Vector2D other) {
        Vector2D(x + other.x, y + other.y)
    }

    method *(Number scalar) {
        Vector2D(x * scalar, y * scalar)
    }

    method magnitude {
        (x**2 + y**2).sqrt
    }

    method to_s { "<#{x}, #{y}>" }
}

var v1 = Vector2D(1, 2)
var v2 = Vector2D(3, 4)

say (v1 + v2)        # <4, 6>
say (v1 * 3)         # <3, 6>
say v2.magnitude     # 5

9. Special Numeric Types

Modular Arithmetic (Mod)

Mod(n, m) creates a number in ℤ/mℤ — all operations are automatically reduced modulo m:

var a = Mod(13, 19)
var b = Mod(7,  19)

say (a + b)     # Mod(1, 19)   (13+7 = 20 ≡ 1 mod 19)
say (a * b)     # Mod(15, 19)
say a**100      # Mod(6, 19)   — fast modular exponentiation

# Modular inverse
say a.inv     # Mod(3, 19)  since 13*3 = 39 ≡ 1 mod 19

Mod is ideal for cryptographic and number-theoretic computations:

# Fermat's little theorem: a^(p-1) ≡ 1 (mod p) for prime p, gcd(a,p)=1
var p = 97
var a = Mod(42, p)
say a**(p-1)    # Mod(1, 97)

Gaussian Integers (Gauss)

Gaussian integers are complex numbers a + bi where both a and b are integers:

var g1 = Gauss(3, 4)
var g2 = Gauss(1, -2)

say (g1 + g2)   # Gauss(4, 2)
say (g1 * g2)   # Gauss(11, -2)   (= 3-6i+4i+8 = 11-2i)
say g1.norm     # 25              (= 3² + 4²)
say g1.conj     # Gauss(3, -4)

# Factorize a Gaussian integer
say Gauss(5, 0).factor    # Gauss(2+i) * Gauss(2-i)

Polynomials (Poly)

var p = Poly([1, -3, 2])     # x² - 3x + 2
var q = Poly([1, 1])         # x + 1

say (p * q)                  # x³ - 2x² - x + 2
say p.eval(0)                # 2
say p.eval(1)                # 0   (1 is a root)
say p.eval(2)                # 0   (2 is a root)

# Polynomial from roots
say Poly.from_roots([1, 2, 3])    # x³ - 6x² + 11x - 6

# Derivative
say p.derivative    # 2x - 3

Modular Polynomials (PolyMod)

Polynomials over a finite field:

var p = PolyMod([1, 0, 1], 2)     # x² + 1 over GF(2)
var q = PolyMod([1, 1],    2)     # x + 1  over GF(2)

say (p * q)    # x³ + x² + x + 1 (mod 2)

Quaternions

var q1 = Quaternion(1, 2, 3, 4)    # 1 + 2i + 3j + 4k
var q2 = Quaternion(5, 6, 7, 8)

say (q1 + q2)  # Quaternion(6, 8, 10, 12)
say (q1 * q2)  # Quaternion(-60, 12, 30, 24)
say q1.norm    # sqrt(1+4+9+16) = sqrt(30)
say q1.conj    # Quaternion(1, -2, -3, -4)

10. Number Theory

Sidef has exceptional built-in support for number-theoretic functions — far beyond what most languages offer out of the box.

Primality and Prime Generation

say (2**31 - 1)               # 2147483647
say (2**31 - 1 -> is_prime)   # true (Mersenne prime)

# nth prime
say 1000.prime    # 7919

# Primes in a range
say primes(1000, 1100)    # all primes between 1000 and 1100

# Prime counting function π(n)
say prime_count(10**6)    # 78498

Factorization

say factor(12)              # [2, 2, 3]
say factor(2**64 + 1)       # full factorization of large numbers

# Factor as pairs [prime, exponent]
say factor_exp(360)         # [[2,3],[3,2],[5,1]]

# Euler's totient φ(n)
say euler_phi(360)          # 96

# Number of divisors σ₀(n)
say sigma(360, 0)           # 24

# Sum of divisors σ₁(n)
say sigma(360, 1)           # 1170

# Sum of squares of divisors σ₂(n)
say sigma(360, 2)           # 63050

# List of divisors
say divisors(36)            # [1, 2, 3, 4, 6, 9, 12, 18, 36]

GCD, LCM, and Extended GCD

say gcd(48, 18)         # 6
say lcm(4, 6)           # 12
say gcd(0, 7)           # 7

# Extended Euclidean algorithm: returns (x, y, gcd) where gcd = a*x + b*y
var (x, y, g) = 48.gcdext(18)
say "#{g} = 48*#{x} + 18*#{y}"    # 6 = 48*(-1) + 18*3

# Modular inverse: x such that a*x ≡ 1 (mod m)
say invmod(7, 11)    # 8  (since 7*8 = 56 ≡ 1 mod 11)

Modular Arithmetic Functions

# Modular exponentiation: base^exp mod m
say powmod(2, 100, 10**9 + 7)

# Jacobi symbol
say jacobi(5, 11)    # 1  (5 is a quadratic residue mod 11)

# Modular square root (Tonelli-Shanks)
say sqrtmod(4, 7)    # 2  (2² ≡ 4 mod 7)

# Multiplicative order: smallest k > 0 with a^k ≡ 1 (mod m)
say znorder(2, 7)    # 3

Sieve of Eratosthenes (Manual Implementation)

This example shows how to implement a sieve yourself to understand how Sidef's builtins work under the hood:

func sieve(limit) {
    var composite = limit.of(false)
    var primes    = []

    for p in (2..limit.isqrt) {
        if (!composite[p]) {
            var j = p*p
            while (j <= limit) {
                composite[j] = true
                j += p
            }
        }
    }

    for n in (2..limit) {
        primes << n if !composite[n]
    }
    primes
}

say sieve(50)    # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

Miller-Rabin Primality Test (Manual)

func miller_rabin(n, witnesses) {
    return false if (n < 2)
    return true  if ((n == 2) || (n == 3))
    return false if (n.is_even)

    # Write n-1 as 2^r * d
    var (r, d) = (0, n - 1)
    while (d.is_even) { d >>= 1; ++r }

    for a in witnesses {
        next if (a >= n)
        var x = powmod(a, d, n)
        next if ((x == 1) || (x == n-1))

        var composite = true
        (r-1).times {
            x = powmod(x, 2, n)
            if (x == n-1) { composite = false; break }
        }
        return false if composite
    }
    true
}

# Deterministic for n < 3,215,031,751 with these witnesses:
func is_prime_mr(n) {
    miller_rabin(n, [2, 3, 5, 7])
}

say is_prime_mr(104729)     # true
say is_prime_mr(104731)     # false
say 25.by(is_prime_mr)      # first 25 primes

Chinese Remainder Theorem

Find x such that x ≡ a₁ (mod m₁) and x ≡ a₂ (mod m₂), etc.:

# Built-in CRT
say Math.chinese([2,3], [3,5], [2, 7])    # 23  (23≡2 mod 3, 23≡3 mod 5, 23≡2 mod 7)

# Manual CRT for two congruences
func crt2(a1, m1, a2, m2) {
    var (u, _, g) = m1.gcdext(m2)
    die "No solution" if ((a2 - a1) % g != 0)
    var lcm = m1.lcm(m2)
    ((a1 + (m1 * u * ((a2 - a1) / g))) % lcm + lcm) % lcm
}

say crt2(3, 5, 5, 7)    # 33  (33≡3 mod 5, 33≡5 mod 7)

Arithmetic Functions and Multiplicativity

# Möbius function μ(n)
say moebius(1)     #  1
say moebius(6)     #  1   (6 = 2·3, squarefree, two prime factors)
say moebius(12)    #  0   (12 = 2²·3, not squarefree)
say moebius(30)    # -1   (30 = 2·3·5, three prime factors)

# Liouville function λ(n)
say liouville(12)    # 1   (Ω(12) = 3, (-1)³ = -1? — check actual output)

# Omega functions
say omega(12)       # 2   (distinct prime factors: 2, 3)
say bigomega(12)    # 3   (with multiplicity: 2,2,3)

# Sum over divisors
var n = 100
say divisors(n).sum               # 217  (sigma(100))
say divisors(n).sum{.euler_phi}   # == n   (Gauss identity)

11. Arbitrary Precision and Floating-Point

Sidef's rationals are exact, but you can explicitly use arbitrary-precision floats when needed.

Exact Rational Arithmetic

# Rationals never accumulate floating-point error
say (1/3 + 1/3 + 1/3 == 1)    # true
say (0.1 + 0.2 == 0.3)        # true

var r = 355/113
say r                          # 355/113
say r.as_float                 # 3.14159292...
say r.numerator                # 355
say r.denominator              # 113

High-Precision Floats

# Dynamic precision change (local scope)
local Num!PREC = 1000.numify    # 1000 bits ≈ 301 decimal places

say Num.pi        # π to 300+ places
say exp(1)        # e to 300+ places
say sqrt(2)       # √2 to 300+ places

Command-line precision:

sidef -P200 -e 'say pi'    # 200 decimal places of π

Computing π via the AGM

The arithmetic-geometric mean converges doubly-exponentially to π:

local Num!PREC = 200.numify

func agm_pi {
    var a = 1
    var b = (1 / sqrt(2))
    var t = 0.25
    var p = 1f

    64.times {
        var a1 = ((a + b) / 2)
        var b1 = sqrt(a * b)
        var t1 = (t - (p * (a - a1)**2))
        (a, b, t, p) = (a1, b1, t1, p*2)
    }

    (a + b)**2 / (4 * t)
}

say agm_pi()

Continued Fractions

# Represent a number as a continued fraction
say (355/113 -> cfrac)     # [3, 7, 16]

# Convergents of √2
var sqrt2_cf = Enumerator({ |yield|
    var (p0, p1, q0, q1) = (1, 1, 0, 1)
    yield(p1/q1)
    loop {
        var (a, b) = (p1 + p0, q1 + q0)
        # Determine next coefficient via the actual CF expansion of √2
        var coeff = (((a.float / b.float) > sqrt(2.float)) ? 1 : 2)
        (p0, p1) = (p1, coeff*p1 + p0)
        (q0, q1) = (q1, coeff*q1 + q0)
        yield(p1/q1)
    }
})

# Best rational approximations to √2
sqrt2_cf.first(8).each { |r|
    say "#{r.as_frac}\t≈  #{r.round(-8)}"
}

12. Functional Programming Patterns

Function Composition

func compose(*fns) {
    func(x) {
        fns.reverse.reduce({ |acc, f| f(acc) }, x)
    }
}

var process = compose(
    func(x) { x * 2 },
    func(x) { x + 3 },
    func(x) { x ** 2 },
)

say process(4)    # ((4**2) + 3) * 2 = 38

Memoization with is cached

Cache is automatically invalidated per unique argument tuple:

func count_partitions(n, k = n) is cached {
    return 1 if (n == 0)
    return 0 if ((n < 0) || (k == 0))
    count_partitions(n - k, k) + count_partitions(n, k - 1)
}

say count_partitions(20)    # 627
say count_partitions(50)    # 204226

Trampolining (Avoid Stack Overflow for Deep Recursion)

For very deep recursion you can use an explicit stack:

func flatten_deep(arr) {
    var result = []
    var stack  = [arr]

    while (stack) {
        var item = stack.pop
        if (item.is_a(Array)) {
            stack.push(item...)
        } else {
            result.unshift(item)
        }
    }
    result
}

say flatten_deep([1, [2, [3, [4, [5]]]], 6])    # [1, 2, 3, 4, 5, 6]

Transducer-Style Pipeline

# Composable pipeline using closures
func filtering(pred) { func(acc, x) { pred(x) ? (acc << x) : acc } }
func mapping(f)      { func(acc, x) { acc << f(x) } }

var xform = [
    filtering({ _ > 3 }),
    mapping({ _ ** 2 }),
    filtering({ _ < 100 }),
]

var input = 1..10
var result = input.reduce({ |acc, x|
    xform.reduce({ |a, f| f(a, x) }, acc)
}, [])

say result    # [16, 25, 36, 49, 64, 81]

Currying

func curry(f, *bound) {
    func(*args) { f(bound..., args...) }
}

func add(a, b, c) { a + b + c }

var add5 = curry(:add, 5)
var add5_10 = curry(:add, 5, 10)

say add5(3, 2)     # 10
say add5_10(7)     # 22

13. Regular Expressions

Capture Groups and Named Captures

var date = "2024-03-15"

# Indexed captures
var m = date.match(/(\d{4})-(\d{2})-(\d{2})/)
say m[0]    # 2024
say m[1]    # 03
say m[2]    # 15

# Named captures
var n = date.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/)
say n{:year}    # 2024
say n{:month}   # 03

Global Matching and gmatch

var text = "The price is \$3.99 and \$12.50 and \$0.75"

# Collect all matches
var prices = []
while (var m = text.match(/\$(\d+\.\d+)/g)) {
    prices << m[0].to_r
}
say prices         # [3.99, 12.5, 0.75]
say prices.sum     # 17.24

Substitution

var s = "Hello, World!"

# Replace first
say s.sub(/World/, "Sidef")        # Hello, Sidef!

# Replace all (g flag)
say "aabbcc".gsub(/([a-z])\1/, { |m| m[0].uc })    # AaBbCc — hmm
say "banana".gsub(/a/, "o")        # bonono

# In-place edit on a file
File("config.txt").edit { |line|
    line.gsub(/localhost/, "127.0.0.1")
}

Smart Matching ~~

"hello" ~~ /^h/            # true  — regex
"foo"   ~~ "foobar"        # false — substring?
"a"     ~~ %w(a b c)       # true  — element of array
/^b/    ~~ %w(foo bar)     # true  — any element matches
42      ~~ (1..100)         # true  — in range

Splitting and Joining

"one two  three".split(/\s+/)         # ["one","two","three"]
"a1b2c3".split(/(?<=\d)(?=\D)/)       # ["a1","b2","c3"]

var csv = "name,age,city"
var fields = csv.split(',')
say fields.join(" | ")    # name | age | city

14. File and Directory I/O

Reading Files

# Entire file as string
var content = File("data.txt").read

# Line by line (memory efficient)
File("data.txt").each_line { |line|
    say line.trim
}

# All lines as array
var lines = File("data.txt").lines

# Read and parse a CSV-like file
File("data.csv").each_line { |line|
    var (name, age, city) = line.trim.split(',')...
    say "#{name} is #{age} years old, lives in #{city}"
}

Writing Files

File("out.txt").write("First line\n")
File("out.txt").append("Second line\n")

# Write multiple lines
var lines = (1..5).map { "Line #{_}" }
File("out.txt").write(lines.join("\n") + "\n")

Directory Operations

# List files in a directory
Dir(".").each { |entry|
    say entry if entry =~ /\.sf$/
}

# Recursive file finder
func find_files(dir, pattern) {
    gather {
        Dir(dir).each_r { |f|
            take(f) if (f =~ pattern)
        }
    }
}

find_files(".", /\.sf$/).each { |f| say f }

File Metadata

var f = File("script.sf")

say f.exists        # true/false
say f.size          # size in bytes
say f.mtime         # modification time
say f.is_file       # true
say f.is_dir        # false
say f.abs_path      # absolute path

Pipe and Shell Integration

# Run a command and iterate its output lines
Pipe("ls -la").each_line { |line|
    say line if line =~ /\.sf$/
}

# Capture output
var git_log = %x(git log --oneline -10)
git_log.lines.each { |line| say line.trim }

15. Perl Module Integration

Any CPAN module can be used directly, making Sidef's ecosystem enormous.

Object-Oriented Perl Modules

# HTTP requests
var ua       = require('LWP::UserAgent').new
var response = ua.get('https://api.github.com')
say response.decoded_content

# JSON encoding/decoding
var json    = require('JSON')
var encoded = json.encode(Hash(name => "Alice", scores => [98, 87, 92]))
say encoded

var decoded = json.decode('{"x":1,"y":2}')
say decoded{:x}    # 1

Functional Perl Modules

var posix = frequire('POSIX')
say posix.floor(3.7)    # 3
say posix.ceil(3.2)     # 4

var list_util = frequire('List::Util')
say list_util.sum(1..10)    # 55
say list_util.max(3,1,4,1,5,9,2,6)    # 9
say list_util.shuffle([1..10]...)

Using Perl's DBI for Databases

var dbi = require('DBI')
var dbh = dbi.connect("dbi:SQLite:dbname=test.db", "", "")

dbh.do("CREATE TABLE IF NOT EXISTS users (id INTEGER, name TEXT)")
dbh.do("INSERT INTO users VALUES (1, 'Alice')")

var sth = dbh.prepare("SELECT * FROM users WHERE id = ?")
sth.execute(1)

while (var row = sth.fetchrow_hashref) {
    say "#{row{:id}}: #{row{:name}}"
}
dbh.disconnect

16. Sorting Algorithms

Sidef makes it easy to implement and compare classic algorithms.

Quicksort

func quicksort(arr) {
    return arr if (arr.len <= 1)
    var pivot = arr[arr.len >> 1]
    var left  = arr.grep { _ < pivot }
    var mid   = arr.grep { _ == pivot }
    var right = arr.grep { _ > pivot }
    [quicksort(left)..., mid..., quicksort(right)...]
}

say quicksort([3, 6, 8, 10, 1, 2, 1])    # [1, 1, 2, 3, 6, 8, 10]

Merge Sort

func merge(a, b) {
    var result = []
    while (a && b) {
        result << (a[0] <= b[0] ? a.shift : b.shift)
    }
    [result..., a..., b...]
}

func mergesort(arr) {
    return arr if (arr.len <= 1)
    var mid = arr.len>>1
    merge(mergesort(arr.slice(0, mid)), mergesort(arr.slice(mid)))
}

say mergesort([5, 2, 8, 1, 9, 3])    # [1, 2, 3, 5, 8, 9]

Radix Sort

func radix_sort(arr, base = 10) {
    return arr if (arr.len <= 1)
    var max_val = arr.max
    var exp = 1

    while (max_val / exp >= 1) {
        var buckets = base.of { [] }
        arr.each { |n|
            buckets[(n // exp) % base] << n
        }
        arr = buckets.flat
        exp *= base
    }
    arr
}

say radix_sort([170, 45, 75, 90, 802, 24, 2, 66])
# [2, 24, 45, 66, 75, 90, 170, 802]

Sorting with Schwartzian Transform

# Sort strings by their vowel count (efficient — compute key once)
var words = %w(programming sidef beautiful algorithm lazy quick)

var sorted = words \
    .map { |w| [w, w.count(/[aeiou]/)] } \
    .sort_by { _[1] } \
    .map { _[0] }

say sorted    # sorted from fewest to most vowels

17. Dynamic Programming and Memoization

Longest Common Subsequence

func lcs(String a, String b) is cached {
    return ""  if (a.is_empty || b.is_empty)
    if (a[-1] == b[-1]) {
        lcs(a.slice(0, -1), b.slice(0, -1)) + a[-1]
    } else {
        var s1 = lcs(a.slice(0, -1), b)
        var s2 = lcs(a, b.slice(0, -1))
        s1.len >= s2.len ? s1 : s2
    }
}

say lcs("ABCBDAB", "BDCABA")    # BCBA

Edit Distance (Levenshtein)

func edit_distance(String a, String b) is cached {
    return b.len if a.is_empty
    return a.len if b.is_empty

    if (a[0] == b[0]) {
        edit_distance(a.slice(1), b.slice(1))
    } else {
        1 + [
            edit_distance(a.slice(1), b),           # delete
            edit_distance(a, b.slice(1)),           # insert
            edit_distance(a.slice(1), b.slice(1)),  # replace
        ].min
    }
}

say edit_distance("kitten", "sitting")    # 3
say edit_distance("Sunday", "Saturday")   # 3

0/1 Knapsack Problem

func knapsack(capacity, weights, values, n) is cached {
    return 0 if ((n == 0) || (capacity == 0))

    if (weights[n-1] > capacity) {
        knapsack(capacity, weights, values, n-1)
    } else {
        [
            values[n-1] + knapsack(capacity - weights[n-1], weights, values, n-1),
            knapsack(capacity, weights, values, n-1),
        ].max
    }
}

var weights  = [2, 3, 4, 5]
var values   = [3, 4, 5, 6]
var capacity = 8

say knapsack(capacity, weights, values, weights.len)    # 10

18. Matrix and Vector Arithmetic

Matrix Basics

var A = Matrix([[1,2],[3,4]])
var B = Matrix([[5,6],[7,8]])

say A+B       # [[6,8],[10,12]]
say A*B       # [[19,22],[43,50]]
say A.T       # transpose: [[1,3],[2,4]]
say A.det     # determinant: -2
say A.inv     # inverse

Matrix Exponentiation (Fast Fibonacci)

Matrix exponentiation runs in O(log n) and can compute Fibonacci numbers efficiently:

func matmul(A, B) {
    var n = A.len
    n.of { |i|
        n.of { |j|
            ^n -> sum { |k| A[i][k] * B[k][j] }
        }
    }
}

func matpow(M, n) {
    return M if (n == 1)
    var half = matpow(M, n >> 1)
    var sq   = matmul(half, half)
    n.is_odd ? matmul(sq, M) : sq
}

func fib_fast(n) {
    return n if (n <= 1)
    matpow([[1,1],[1,0]], n)[0][1]
}

say fib_fast(100)    # 354224848179261915075
say fib_fast(1000)   # (a very large number)

Solving Linear Systems

# Ax = b  →  x = A⁻¹ b
var A = Matrix([2, 1], [5, 7])
var b = Matrix([11], [13])

var x = (A.inv * b)
say x    # [[3], [-1]]  — solution: x₁=3, x₂=-1

19. Encoding, Compression, and Cryptography Patterns

Run-Length Encoding

func rle_encode(String s) {
    gather {
        var chars = s.chars
        var i = 0
        while (i < chars.len) {
            var c = chars[i]
            var count = 1
            while ((i+count < chars.len) && (chars[i+count] == c)) {
                ++count
            }
            take([c, count])
            i += count
        }
    }
}

func rle_decode(pairs) {
    pairs.map { |p| p[0] * p[1] }.join
}

var encoded = rle_encode("aaabbbccddddee")
say encoded    # [["a",3],["b",3],["c",2],["d",4],["e",2]]
say rle_decode(encoded)    # aaabbbccddddee

Caesar Cipher

func caesar_encrypt(String text, Number shift) {
    text.chars.map { |c|
        if (c =~ /[a-zA-Z]/) {
            var base = (c =~ /[A-Z]/ ? 'A'.ord : 'a'.ord)
            ((c.ord - base + shift) % 26 + base).chr
        } else {
            c
        }
    }.join
}

func caesar_decrypt(String text, Number shift) {
    caesar_encrypt(text, 26 - shift)
}

var msg = "Hello, World!"
var enc = caesar_encrypt(msg, 13)    # ROT13
say enc                              # Uryyb, Jbeyq!
say caesar_decrypt(enc, 13)          # Hello, World!

XOR One-Time Pad (Demonstration)

func xor_crypt(String text, String key) {
    var key_bytes  = key.bytes
    var text_bytes = text.bytes
    var klen       = key_bytes.len

    text_bytes.map_kv { |i, b|
        b ^ key_bytes[i % klen]
    }.map { .chr }.join
}

var plaintext  = "Attack at dawn"
var key        = "SECRET"
var ciphertext = xor_crypt(plaintext, key)
var recovered  = xor_crypt(ciphertext, key)

say (recovered == plaintext)    # true

RSA Key Generation Sketch

# Generate two random primes
var p = random_prime(10**50, 10**51)
var q = random_prime(10**50, 10**51)
var n = (p * q)
var phi = ((p-1) * (q-1))

# Public exponent
var e = 65537
die "Bad e" if (gcd(e, phi) != 1)

# Private exponent
var d = invmod(e, phi)

# Encrypt and decrypt
var msg        = 42
var ciphertext = powmod(msg, e, n)
var decrypted  = powmod(ciphertext, d, n)

say (decrypted == msg)    # true

20. Putting It All Together: Larger Examples

Prime Sieve with Segmented Output

This finds all twin prime pairs up to a given limit, neatly formatted:

func twin_primes(limit) {
    gather {
        var prev = 2
        primes(3, limit).each { |p|
            take([prev, p]) if (p - prev == 2)
            prev = p
        }
    }
}

twin_primes(200).each_kv { |i,pair|
    say "#{ '%3d' % (i+1) }. (#{pair[0]}, #{pair[1]})"
}

Goldbach's Conjecture Verification

Every even integer greater than 2 is the sum of two primes:

func goldbach(n) {
    return nil if (n <= 2 || n.is_odd)
    with (primes(2, n/2).first { |p| (n - p).is_prime }) {|p|
        [p, n-p]
    }
}

for n in (4..50 `by` 2) {
    var (a, b) = goldbach(n)...
    say "#{n} = #{a} + #{b}"
}

Conway's Game of Life

func game_of_life(grid, generations) {
    var rows = grid.len
    var cols = grid[0].len

    func neighbors(r, c) {
        var count = 0
        for dr in (-1..1) {
            for dc in (-1..1) {
                next if ([dr,dc] == [0,0])
                var nr = (r + dr)
                var nc = (c + dc)
                if ((nr >= 0) && (nr < rows) && (nc >= 0) && (nc < cols)) {
                    count += grid[nr][nc]
                }
            }
        }
        count
    }

    func step(g) {
        rows.of { |r|
            cols.of { |c|
                var n = neighbors(r, c)
                (g[r][c] == 1) ? (n ~~ [2,3] ? 1 : 0)
                               : (n == 3 ? 1 : 0)
            }
        }
    }

    generations.times { grid = step(grid) }
    grid
}

# Glider initial state
var glider = [
    [0,1,0,0,0],
    [0,0,1,0,0],
    [1,1,1,0,0],
    [0,0,0,0,0],
    [0,0,0,0,0],
]

func render(g) {
    g.each { |row| say row.map{ _ ? "█" : "·" }.join }
    say ""
}

render(glider)
var next_gen = game_of_life(glider, 1)
render(next_gen)

Dijkstra's Shortest Path

func dijkstra(graph, source) {
    var dist    = Hash()
    var visited = Set()

    graph.keys.each { |v| dist{v} = Inf }
    dist{source} = 0

    loop {
        # Pick unvisited vertex with smallest distance
        var u = graph.keys \
            .grep { !visited.has(_) && (dist{_} < Inf) } \
            .min_by { dist{_} }

        break if (u == nil)
        visited.add(u)

        graph{u}.each { |v, weight|
            var alt = (dist{u} + weight)
            dist{v} = alt if (alt < dist{v})
        }
    }
    dist
}

var graph = Hash(
    A => Hash(B => 4, C => 2),
    B => Hash(D => 3, C => 1),
    C => Hash(B => 1, D => 5),
    D => Hash(),
)

var distances = dijkstra(graph, :A)
distances.keys.sort.each { |v|
    say "A → #{v} : #{distances{v}}"
}
# A → A : 0
# A → B : 3  (A→C→B)
# A → C : 2
# A → D : 6  (A→C→B→D)

Arithmetic Coder (Sketch)

A simplified arithmetic coder demonstrates how frequency models are used in compression:

func build_model(String text) {
    var freq = Hash()
    text.chars.each { |c| freq{c} := 0 ++ }
    var total = text.len

    var model = Hash()
    var cumul = 0
    freq.keys.sort.each { |c|
        var p = (freq{c} / total)
        model{c} = [cumul, cumul + p]
        cumul += p
    }
    model
}

var model = build_model("aabbbc")
model.keys.sort.each { |c|
    say "#{c}: [#{model{c}[0].round(-4)}, #{model{c}[1].round(-4)})"
}

21. Further Resources

Official Documentation

Example Script Collections

  • 📂 sidef-scripts — Hundreds of real Sidef programs organized by category:
    • Math/ — Number theory, factorization algorithms, primality tests, special functions
    • Encoding/ — Huffman, arithmetic coding, BWT, LZW, run-length encoding
    • Encryption/ — RSA, one-time pad, XOR
    • Compression/ — Gzip, bzip2, LZ77, LZW compressors
    • Games/ — Conway's Game of Life, snake, Bulls and Cows
    • Graph/ — Dijkstra, Kosaraju's SCC algorithm
    • Genetic/ — Genetic algorithms
    • Image/ — Fractal generation (Sierpinski, Koch, Mandelbrot, Barnsley fern)
    • Sort/ — Classic sorting algorithms

Community


Happy coding with Sidef! 🚀

Guide for intermediate and advanced users — assumes Sidef is already installed