#11: Lists in Elixir vs. Arrays in Ruby

·

4 min read

In this post, I am going to dive deeper into the topic of data types, in particular lists, and compare them to arrays in Ruby. I am also briefly explaining the concept of linked lists as Elixir lists are essentially linked lists and thus fundamentally different from arrays in Ruby.

Lists in Elixir:

Definition: Lists in Elixir are ordered collections of elements enclosed in square brackets [ ]. They are immutable and commonly used for functional programming tasks.

Characteristics:

Lists are implemented as linked lists, allowing for efficient pattern matching and recursion. Operations on lists return new lists, preserving the original data and promoting immutability.

Example:

# Creating a list in Elixir
list = [1, 2, 3, 4]

# Pattern matching with lists
[head | tail] = list
IO.puts "Head: #{head}"  # Output: Head: 1
IO.inspect "Tail: #{tail}"  # Output: Tail: [2, 3, 4]

What are Linked Lists?

Linked lists are a fundamental data structure consisting of nodes where each node contains a value and a reference (or link) to the next node in the sequence. In linked lists, elements are not stored in contiguous memory locations like arrays; instead, each element points to the next element in the sequence.

In the context of Elixir lists, Elixir uses linked lists to represent lists. Elixir lists are singly linked lists where each element in the list points to the next element until the end of the list is reached. This design allows for efficient insertion and deletion at the beginning of the list but can be less efficient for random access.

Here is a simple code example in Elixir to demonstrate the concept of linked lists using Elixir lists:

# Creating a linked list in Elixir
list = [1, 2, 3, 4, 5]

# Accessing elements in the linked list
IO.inspect hd(list) # Output: 1 (head of the list)
IO.inspect tl(list) # Output: [2, 3, 4, 5] (tail of the list)

# Alternatively, we can make use of Elixir's pattern matching:
[head | tail] = list
IO.inspect head # Output: 1 (head of the list)
IO.inspect tail # Output: [2, 3, 4, 5] (tail of the list)

# Adding an element to the beginning of the linked list
new_list = [0 | list]
IO.inspect new_list # Output: [0, 1, 2, 3, 4, 5]

# Removing the first element from the linked list
updated_list = tl(list)
IO.inspect updated_list # Output: [2, 3, 4, 5]

In this example, we create a linked list [1, 2, 3, 4, 5] in Elixir. We then demonstrate accessing the head and tail of the list using hd and tl functions or Elixir's pattern-matching capabilities. We add a new element 0 to the beginning of the list using the cons operator |, and we remove the first element from the list using the tl function.

This code snippet showcases the concept of linked lists in Elixir lists, where elements are linked together through references to form a sequence.

Arrays in Ruby:

Definition: Arrays in Ruby are ordered collections of elements enclosed in square brackets [ ]. They are mutable and commonly used for data manipulation tasks.

Characteristics:

Ruby arrays provide fast random access to elements by index, making them suitable for various data manipulation operations. Arrays can be modified in place, allowing for dynamic changes to the data structure.

Example:

# Creating an array in Ruby
array = [1, 2, 3, 4]

# Accessing elements in an array
puts "First element: #{array[0]}"  # Output: First element: 1
puts "Last element: #{array[-1]}"  # Output: Last element: 4

# Modifying an array
array.push(5)
puts "Array after push: #{array}"  # Output: Array after push: [1, 2, 3, 4, 5]

Differences and Use Cases:

  1. Immutability vs. Mutability:

    Elixir lists are immutable, ensuring data integrity and avoiding side effects. Ruby arrays are mutable, allowing for in-place modifications.

  2. Functional Programming vs. Data Manipulation:

    Elixir lists are well-suited for functional programming tasks like recursion and pattern matching. Ruby arrays are commonly used for data manipulation tasks requiring dynamic changes.

  3. Performance and Paradigms:

    Understanding the immutability of Elixir lists and the mutability of Ruby arrays is crucial for choosing the appropriate data structure based on the programming paradigm and performance requirements.

I hope that the above examples have given you a grasp of the fundamental differences in immutability, mutability, and usage scenarios of these data structures in both Elixir and Ruby.