DEV Community

Cover image for Parsing Grid-Based STDIN Input in Gleam
Piyush Chauhan
Piyush Chauhan

Posted on

Parsing Grid-Based STDIN Input in Gleam

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
  })
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Best Practices and Tips

  1. Error Handling: Always provide meaningful error types
  2. Immutability: Use recursive approaches to build data structures
  3. Type Safety: Leverage Gleam's strong type system
  4. 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
Enter fullscreen mode Exit fullscreen mode

Large Integer Inputs

1000000000
-2147483648 (32-bit integer min)
2147483647 (32-bit integer max)
Enter fullscreen mode Exit fullscreen mode

Floating Point Inputs

3.14159
-0.001
2.0
1e-9 (scientific notation)
Enter fullscreen mode Exit fullscreen mode

Multiple Number Inputs

5 10
3 7 2 9 1
Enter fullscreen mode Exit fullscreen mode

2. String Inputs

Basic String

hello
world
competitive
Enter fullscreen mode Exit fullscreen mode

Multiline String

Hello
World
Programming
Enter fullscreen mode Exit fullscreen mode

String with Special Characters

Hello, World!
a@b#c$d%e
programming_contest
Enter fullscreen mode Exit fullscreen mode

String Manipulation Inputs

abracadabra
racecar
Enter fullscreen mode Exit fullscreen mode

3. Array Inputs

1D Integer Array

5
1 2 3 4 5
Enter fullscreen mode Exit fullscreen mode

1D String Array

3
apple banana cherry
Enter fullscreen mode Exit fullscreen mode

2D Integer Array (Matrix)

3 4
1 2 3 4
5 6 7 8
9 10 11 12
Enter fullscreen mode Exit fullscreen mode

Ragged 2D Array

3
2 1 3
4 5
6 7 8 9
Enter fullscreen mode Exit fullscreen mode

4. Graph Inputs

Adjacency List Representation

5 6
1 2
1 3
2 4
3 4
4 5
2 5
Enter fullscreen mode Exit fullscreen mode

Weighted Graph

4 3
1 2 10
2 3 15
3 4 20
Enter fullscreen mode Exit fullscreen mode

Directed Graph

5 5
1 2
2 3
3 4
4 5
1 5
Enter fullscreen mode Exit fullscreen mode

5. Tree Inputs

Parent Array Representation

5
-1 0 0 1 1
Enter fullscreen mode Exit fullscreen mode

Edge List Representation

4
1 2
1 3
2 4
2 5
Enter fullscreen mode Exit fullscreen mode

6. String Parsing Inputs

CSV-like Input

John,25,Engineer
Alice,30,Designer
Bob,22,Developer
Enter fullscreen mode Exit fullscreen mode

Space-Separated Values

name age city
John 25 NewYork
Alice 30 London
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Range Query Inputs

5 3
1 5 3 7 9
2 4
1 3
3 5
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

9. Bit Manipulation Inputs

5
10101
11001
00110
Enter fullscreen mode Exit fullscreen mode

10. Dynamic Programming Inputs

6 10 // Number of items, maximum weight
1 4 5 // Values of items
3 4 5 // Weights of items
Enter fullscreen mode Exit fullscreen mode

Happy coding! 🚀👩‍💻👨‍💻

Top comments (0)