You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
4.3 KiB
Go
147 lines
4.3 KiB
Go
/*
|
|
* Note: This program is written to demonstrate the ideas behind channels and synronization.
|
|
* It isn't written to be efficient code in term of memory use (I use copy by value, not by reference)
|
|
* or in terms of runtime (I use a waitgroup where reading from channels could provides synronization instead).
|
|
*
|
|
* Using slice.Sort in mysort() followed by a 4 way merge in main() is particularly ugly.
|
|
*
|
|
* Main() is split into 6 main steps:
|
|
* Step 1: Get the list of ints from the user
|
|
* Step 2: Split the list into four partitions of approximately equal size
|
|
* Step 3: Pass each quater to a goroutine to sort
|
|
* Step 4: Fetch results from channels
|
|
* Step 5: Merge the results. N.B. I'm taking "merge the 4 sorted subarrays into one large sorted array." literally here.
|
|
* Step 6: Print the output
|
|
*
|
|
* Some shuffled sequences to test this on:
|
|
* 2 5 3 6 1 7 10 4 8 9
|
|
* 3 13 1 11 19 17 16 7 10 14 9 4 6 8 5 2 12 15 18 20
|
|
* 24 18 5 15 4 12 28 30 3 27 29 14 20 6 23 21 10 8 19 9 17 16 7 25 11 2 22 1 26 13
|
|
*
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"bufio"
|
|
"strings"
|
|
"strconv"
|
|
"sync"
|
|
"slices"
|
|
)
|
|
|
|
func mysort(wg *sync.WaitGroup, c chan []int, goroutineNum int, numbers []int) {
|
|
defer wg.Done()
|
|
|
|
fmt.Printf("mysort%d got: %v\n", goroutineNum, numbers)
|
|
|
|
slices.Sort(numbers) // Using the sort in slices as this isn't an exercise in writing a sort()
|
|
c <- numbers
|
|
fmt.Printf("mysort%d returned: %v\n", goroutineNum, numbers)
|
|
}
|
|
|
|
func main() {
|
|
numbers := make([]int, 0)
|
|
|
|
// Step 1: Get the list of ints from the user
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
|
|
fmt.Printf("\nPlease enter a list of integers separated by spaces ( e.g. \"1 3 5 2\" ):\n")
|
|
|
|
scanner.Scan()
|
|
line := scanner.Text()
|
|
fields := strings.Fields(line)
|
|
|
|
for _, v := range(fields) {
|
|
n, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
fmt.Println("Ignoring string: " + v)
|
|
continue
|
|
}
|
|
numbers = append(numbers, n)
|
|
}
|
|
|
|
// Step 2: Split the list into four partitions of approximately equal size
|
|
quarter1 := numbers[:len(numbers)/4]
|
|
quarter2 := numbers[len(numbers)/4 : len(numbers)/2]
|
|
quarter3 := numbers[len(numbers)/2 : len(numbers)/4+len(numbers)/2]
|
|
quarter4 := numbers[len(numbers)/4+len(numbers)/2:]
|
|
|
|
// Step 3: Pass each quater to a goroutine to sort
|
|
// N.B. the waitgroups are not needed for syncronization here as reading from unbuffered channels in Step 4
|
|
// would perform the same function. I'm using buffered channels just so the waitgroup is needed.
|
|
// This is to demonstrate the idea of waitgroups.
|
|
c1 := make(chan []int, 1)
|
|
c2 := make(chan []int, 1)
|
|
c3 := make(chan []int, 1)
|
|
c4 := make(chan []int, 1)
|
|
var wg sync.WaitGroup
|
|
wg.Add(4)
|
|
go mysort(&wg, c1, 1, quarter1)
|
|
go mysort(&wg, c2, 2, quarter2)
|
|
go mysort(&wg, c3, 3, quarter3)
|
|
go mysort(&wg, c4, 4, quarter4)
|
|
wg.Wait()
|
|
|
|
// Step 4: Fetch results from channels
|
|
nums1 := <- c1
|
|
nums2 := <- c2
|
|
nums3 := <- c3
|
|
nums4 := <- c4
|
|
|
|
// Step 5: Merge the results. N.B. I'm taking "merge the 4 sorted subarrays into one large sorted array." literally here.
|
|
// It would be far easier, and more readable, to merge nums1 and nums2 into nums5, merge nums3 and nums4 into nums6,
|
|
// and then merge nums5 and nums6 into the final output.
|
|
sorted := []int{}
|
|
lowest_list := 0
|
|
lowest_number := 0
|
|
for len(sorted) < len(numbers) {
|
|
// Step 5a: Find which of nums1 to nums4 starts with the lowest number
|
|
if len(nums1) > 0 {
|
|
lowest_list = 1;
|
|
lowest_number = nums1[0];
|
|
}
|
|
if len(nums2) > 0 {
|
|
if (len(nums1) == 0) || nums2[0] < lowest_number {
|
|
lowest_list = 2
|
|
lowest_number = nums2[0]
|
|
}
|
|
}
|
|
if len(nums3) > 0 {
|
|
if (len(nums1) == 0 && len(nums2) == 0) || nums3[0] < lowest_number {
|
|
lowest_list = 3
|
|
lowest_number = nums3[0]
|
|
}
|
|
}
|
|
if len(nums4) > 0 {
|
|
if (len(nums1) == 0 && len(nums2) == 0 && len(nums3) == 0) || nums4[0] < lowest_number {
|
|
lowest_list = 4
|
|
lowest_number = nums4[0]
|
|
}
|
|
}
|
|
|
|
// Step 5b: Update sorted with the lowest found and offset the []int it was found in
|
|
sorted = append(sorted, lowest_number)
|
|
switch lowest_list {
|
|
case 1:
|
|
nums1 = nums1[1:]
|
|
case 2:
|
|
nums2 = nums2[1:]
|
|
case 3:
|
|
nums3 = nums3[1:]
|
|
case 4:
|
|
nums4 = nums4[1:]
|
|
}
|
|
}
|
|
|
|
// Step 6: Print the output
|
|
|
|
fmt.Println("Final merged output:\n")
|
|
fmt.Println(sorted)
|
|
}
|
|
|
|
|
|
|