Introduction
Parsing grid inputs is a common challenge in programming challenges and data processing tasks. In this article, I'll walk you through creating a robust grid input reader in Gleam, demonstrating error handling, functional programming techniques, and Gleam's powerful type system.
Installing Gleam
>> brew update
>> brew install gleam
>> gleam --version
>> gleam 1.6.3
Setting Up Your Gleam Project
Let's start by creating a new Gleam project and setting up the necessary dependencies:
# Create a new Gleam project
gleam new grid_reader
# Navigate to the project directory
cd grid_reader
# Add Erlang support (Gleam runs on the Erlang VM)
gleam add erlang
# Download dependencies
gleam deps download
The Complete Grid Input Reader
Here's a comprehensive implementation of a grid input reader that handles various potential errors:
grid_reader.gleam
import gleam/erlang
import gleam/int
import gleam/io
import gleam/list
import gleam/result
import gleam/string
// Type alias for our grid
type Grid =
List(List(Int))
// Custom error type for input reading
pub type InputError {
DimensionParseError
GridRowParseError
InputReadError
}
pub fn main() {
case read_grid() {
Ok(grid) -> {
io.println("Grid successfully read:")
print_grid(grid)
}
Error(DimensionParseError) ->
io.println("Error: Could not parse grid dimensions")
Error(GridRowParseError) -> io.println("Error: Could not parse grid row")
Error(InputReadError) -> io.println("Error: Could not read input")
}
}
// Read the grid dimensions and grid itself
pub fn read_grid() -> Result(Grid, InputError) {
use dimensions <- result.try(read_dimensions())
read_grid_rows(dimensions)
}
// Read the first line to get grid dimensions
pub fn read_dimensions() -> Result(#(Int, Int), InputError) {
use line <- result.try(
erlang.get_line("")
|> result.map_error(fn(_) { InputReadError }),
)
let dimensions = string.split(string.trim(line), " ")
case dimensions {
[rows_str, cols_str] -> {
case int.parse(rows_str), int.parse(cols_str) {
Ok(rows), Ok(cols) -> Ok(#(rows, cols))
_, _ -> Error(DimensionParseError)
}
}
_ -> Error(DimensionParseError)
}
}
// Read grid rows based on given dimensions
pub fn read_grid_rows(dimensions: #(Int, Int)) -> Result(Grid, InputError) {
let #(rows, _) = dimensions
read_rows(rows, [])
}
// Recursive helper to read grid rows
pub fn read_rows(remaining: Int, acc: Grid) -> Result(Grid, InputError) {
case remaining {
0 -> Ok(list.reverse(acc))
n -> {
use line <- result.try(
erlang.get_line("")
|> result.map_error(fn(_) { InputReadError }),
)
let row =
string.trim(line)
|> string.split(" ")
|> list.filter_map(int.parse)
case list.length(row) {
0 -> Error(GridRowParseError)
_ -> read_rows(n - 1, [row, ..acc])
}
}
}
}
// Helper function to print the grid
pub fn print_grid(grid: Grid) {
list.each(grid, fn(row) {
let row_str =
list.map(row, fn(cell) { int.to_string(cell) })
|> string.join(" ")
io.println(row_str)
})
}
Key Features of the Grid Input Reader
1. Custom Error Handling
We define a custom InputError
type to provide precise error reporting:
-
DimensionParseError
: Occurs when grid dimensions can't be parsed -
GridRowParseError
: Occurs when a grid row can't be parsed -
InputReadError
: Occurs when input can't be read
2. Functional Error Handling
The implementation uses Gleam's Result
type and result.try
for elegant error propagation.
3. Recursive Row Reading
The read_rows
function uses recursion to read the specified number of rows, building the grid incrementally.
4. Input Parsing
- Trims input lines
- Splits lines by spaces
- Parses integers
- Handles potential parsing failures
Example Usage
Given an input like:
5 5
0 0 0 0 0
0 1 1 1 0
0 0 0 0 0
1 1 1 1 0
0 0 0 0 0
The program will parse and print the grid.
Running and Testing Your Code
# Run the program
gleam run
# Format and check code
gleam format --check src test
# Run tests (if you have any)
gleam test
Output
Grid successfully read:
0 0 0 0 0
0 1 1 1 0
0 0 0 0 0
1 1 1 1 0
0 0 0 0 0
Best Practices and Tips
- Error Handling: Always provide meaningful error types
- Immutability: Use recursive approaches to build data structures
- Type Safety: Leverage Gleam's strong type system
-
Functional Approach: Use higher-order functions like
list.filter_map
Conclusion
Parsing grid inputs in Gleam showcases the language's strengths in functional programming, type safety, and error handling. By breaking down the problem into small, composable functions, we create robust and readable code.
Further Exploration
- Experiment with different input formats
- Add more sophisticated error handling
- Create comprehensive test suite that covers various scenarios for the grid reading functionality
- Create more complex grid processing functions
Exercises
1. Number Inputs
Basic Integer Inputs
42
-17
0
Large Integer Inputs
1000000000
-2147483648 (32-bit integer min)
2147483647 (32-bit integer max)
Floating Point Inputs
3.14159
-0.001
2.0
1e-9 (scientific notation)
Multiple Number Inputs
5 10
3 7 2 9 1
2. String Inputs
Basic String
hello
world
competitive
Multiline String
Hello
World
Programming
String with Special Characters
Hello, World!
a@b#c$d%e
programming_contest
String Manipulation Inputs
abracadabra
racecar
3. Array Inputs
1D Integer Array
5
1 2 3 4 5
1D String Array
3
apple banana cherry
2D Integer Array (Matrix)
3 4
1 2 3 4
5 6 7 8
9 10 11 12
Ragged 2D Array
3
2 1 3
4 5
6 7 8 9
4. Graph Inputs
Adjacency List Representation
5 6
1 2
1 3
2 4
3 4
4 5
2 5
Weighted Graph
4 3
1 2 10
2 3 15
3 4 20
Directed Graph
5 5
1 2
2 3
3 4
4 5
1 5
5. Tree Inputs
Parent Array Representation
5
-1 0 0 1 1
Edge List Representation
4
1 2
1 3
2 4
2 5
6. String Parsing Inputs
CSV-like Input
John,25,Engineer
Alice,30,Designer
Bob,22,Developer
Space-Separated Values
name age city
John 25 NewYork
Alice 30 London
7. Special Input Formats
Grid with Obstacles
5 5
0 0 0 0 0
0 1 1 1 0
0 0 0 0 0
1 1 1 1 0
0 0 0 0 0
Range Query Inputs
5 3
1 5 3 7 9
2 4
1 3
3 5
8. Combination Inputs
Complex Multi-line Input
3 // Number of test cases
5 // Length of first array
1 2 3 4 5 // First array
3 // Length of second array
6 7 8 // Second array
4 // Length of third array
10 11 12 13 // Third array
9. Bit Manipulation Inputs
5
10101
11001
00110
10. Dynamic Programming Inputs
6 10 // Number of items, maximum weight
1 4 5 // Values of items
3 4 5 // Weights of items
Happy coding! 🚀👩💻👨💻
Top comments (0)