Go Channels: Implementing a Range Generator
This challenge focuses on leveraging Go's powerful concurrency primitives, specifically channels, to create a function that generates a sequence of values. You will implement a generator function that returns a channel, allowing you to iterate over a range of numbers in a concurrent and idiomatic Go fashion. This pattern is incredibly useful for producing data streams that can be consumed by other goroutines without blocking the main execution flow.
Problem Description
Your task is to create a Go function named MakeRange that takes two integer arguments: start and end. This function should return a read-only channel of integers (<-chan int).
The channel should emit integers sequentially from start up to (but not including) end. Once all numbers in the range have been sent, the channel must be closed.
The MakeRange function should be implemented as a goroutine that sends values to the channel. This allows the caller to receive values from the channel as they are generated, without waiting for the entire sequence to be computed.
Key Requirements:
- Function Signature:
func MakeRange(start, end int) <-chan int - Channel Emission: Emit integers
start,start + 1, ...,end - 1. - Channel Closure: Close the channel after emitting the last number.
- Goroutine Implementation: The emission logic must run in a separate goroutine.
- Read-only Channel: The returned channel must be of type
<-chan int.
Expected Behavior:
When a caller receives from the returned channel, they should get the next number in the sequence. When the range is exhausted, receiving from the channel should indicate that it's closed (e.g., using the val, ok := <-ch idiom).
Edge Cases:
startis greater than or equal toend: The channel should be closed immediately without emitting any values.
Examples
Example 1:
package main
import "fmt"
func main() {
// Assume MakeRange is implemented as described
for num := range MakeRange(1, 5) {
fmt.Println(num)
}
}
Output:
1
2
3
4
Explanation: The MakeRange function is called with start=1 and end=5. It starts a goroutine that sends 1, then 2, then 3, then 4 to the channel. After sending 4, it closes the channel. The for range loop in main receives these values and prints them until the channel is closed.
Example 2:
package main
import "fmt"
func main() {
// Assume MakeRange is implemented as described
count := 0
for range MakeRange(10, 10) { // start == end
count++
}
fmt.Println("Count:", count)
}
Output:
Count: 0
Explanation: When start is equal to end, the range is empty. The MakeRange function should detect this, close the channel immediately, and emit no values. The for range loop terminates without executing its body.
Example 3:
package main
import "fmt"
func main() {
// Assume MakeRange is implemented as described
count := 0
for range MakeRange(5, 2) { // start > end
count++
}
fmt.Println("Count:", count)
}
Output:
Count: 0
Explanation: Similar to Example 2, when start is greater than end, the range is considered empty. The channel is closed immediately, and no values are sent or received.
Constraints
- The
startandendintegers will be within the standardintrange in Go. - The
MakeRangefunction must be implemented entirely in Go. - Efficiency is important; avoid unnecessary allocations or complex operations. The primary goal is to demonstrate channel usage for generating sequences.
Notes
- Remember to use a
gokeyword to start the goroutine that populates the channel. - The
defer close(ch)pattern within the goroutine is a clean way to ensure the channel is closed. - Think about how the
for rangeloop on a channel behaves when the channel is closed. This is key to making the generator work correctly. - Consider the case where
start >= end. How should your generator handle this gracefully?