first prototype
This commit is contained in:
		
							
								
								
									
										122
									
								
								manualsort.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								manualsort.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,122 @@
 | 
			
		||||
// binary manualsort asks you at the CLI which of two things you like
 | 
			
		||||
// better, repeatedly, and pukes up a list sorted accordingly.
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"slices"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// handwritten merge sort because it minimizes comparisons, which are
 | 
			
		||||
// the expensive part when asking a human for every comparison.
 | 
			
		||||
// quicksort's "in-place" behavior isn't necessary.
 | 
			
		||||
func merge(a, b []string) []string {
 | 
			
		||||
	if len(a) == 0 {
 | 
			
		||||
		return b
 | 
			
		||||
	}
 | 
			
		||||
	if len(b) == 0 {
 | 
			
		||||
		return a
 | 
			
		||||
	}
 | 
			
		||||
	ret := make([]string, 0, len(a)+len(b))
 | 
			
		||||
	for len(a) > 0 && len(b) > 0 {
 | 
			
		||||
		if better(a[0], b[0]) {
 | 
			
		||||
			ret = append(ret, a[0])
 | 
			
		||||
			a = a[1:]
 | 
			
		||||
		} else {
 | 
			
		||||
			ret = append(ret, b[0])
 | 
			
		||||
			b = b[1:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(a) > 0 {
 | 
			
		||||
		ret = append(ret, a...)
 | 
			
		||||
	}
 | 
			
		||||
	if len(b) > 0 {
 | 
			
		||||
		ret = append(ret, b...)
 | 
			
		||||
	}
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	stdinReader *bufio.Reader
 | 
			
		||||
	truthy      = []string{
 | 
			
		||||
		"y", "yes", "1", "t", "true", "aye", "left", "up", "top", "first", "p",
 | 
			
		||||
	}
 | 
			
		||||
	falsey = []string{
 | 
			
		||||
		"n", "no", "0", "2", "f", "false", "nay", "right", "down", "bottom", "second", "nil",
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func better(first, second string) bool {
 | 
			
		||||
	for {
 | 
			
		||||
		fmt.Println()
 | 
			
		||||
		fmt.Println("Is")
 | 
			
		||||
		fmt.Println(first)
 | 
			
		||||
		fmt.Println("better than")
 | 
			
		||||
		fmt.Println(second)
 | 
			
		||||
		fmt.Print("? > ")
 | 
			
		||||
		reply, err := stdinReader.ReadString('\n')
 | 
			
		||||
		if err != nil && len(reply) == 0 {
 | 
			
		||||
			log.Fatalf("I/O error: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		reply = strings.TrimSpace(reply)
 | 
			
		||||
		reply = strings.ToLower(reply)
 | 
			
		||||
		if slices.Contains(truthy, reply) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		if slices.Contains(falsey, reply) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Printf("Huh? I didn't understand %q. Answer \"y\" or \"n\".\n", reply)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func mergeSort(items []string) []string {
 | 
			
		||||
	if len(items) <= 1 {
 | 
			
		||||
		return items
 | 
			
		||||
	}
 | 
			
		||||
	midpoint := len(items) / 2
 | 
			
		||||
	return merge(mergeSort(items[:midpoint]), mergeSort(items[midpoint:]))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	if len(os.Args) != 3 {
 | 
			
		||||
		log.Fatal("wrong number of args, expected 2. Usage: manualsort <item list file> <output file path>")
 | 
			
		||||
	}
 | 
			
		||||
	ifile, err := os.Open(os.Args[1])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("can't open %q: %v.", os.Args[1], err)
 | 
			
		||||
	}
 | 
			
		||||
	ofile, err := os.OpenFile(os.Args[2], os.O_CREATE|os.O_EXCL, 0600)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("can't exclusively create %q: %v.", os.Args[2], err)
 | 
			
		||||
	}
 | 
			
		||||
	stdinReader = bufio.NewReader(os.Stdin)
 | 
			
		||||
 | 
			
		||||
	var items []string
 | 
			
		||||
	scanner := bufio.NewScanner(ifile)
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		if t := strings.TrimSpace(scanner.Text()); len(t) > 0 {
 | 
			
		||||
			items = append(items, t)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := scanner.Err(); err != nil {
 | 
			
		||||
		log.Fatalf("can't read %q: %v", os.Args[1], err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sorted := mergeSort(items)
 | 
			
		||||
	fmt.Println("Sorted. Saving...")
 | 
			
		||||
	obuf := bufio.NewWriter(ofile)
 | 
			
		||||
	for i, s := range sorted {
 | 
			
		||||
		_, err := obuf.WriteString(fmt.Sprintf("%6d %s\n", i+1, s))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Printf("error saving %d (%s): %v\n", i+1, s, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	obuf.Flush()
 | 
			
		||||
	ofile.Sync()
 | 
			
		||||
	ofile.Close()
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user