What can you do in 30 lines of Go? Can you create a useful, complete program that demonstrates its features? [closed]
So, the big buzz in the last few days is Go, the new language from Google. Assuming you're all obsessive programming language geeks like me, you've all downloaded it, built it, and run your "Hello, 世界" program (isn't it nice using a language written by the inventors of UTF-8?). You've all read the tutorial, Effective Go, and some of the other docs.
Now, what are you going to do with it?
I'd like to see some demos that show off the power of Go. What can you do in a brief program? Show off your best example code. While the true measure of a language can't really be taken until you've written and maintained a large codebase with a team of many programmers over the course of a project with changing requirements, seeing how much you can do in a limited amount of code does help to demonstrate the expressive power of a language. I'd like to see short, complete programs that truly exercise the unique new features of Go; not just snippets or "Hello, World".
So, post some cool code you've written with Go. Take advantage of its unique features, like its goroutines and channels for concurrency, or its interface based type system. Can you write a primitive chat server, or cool IRC bot? Implement a parallel Mandelbrot set that scales to many cores? Write an interpreter for some tiny language? And can you do it all in 30 lines?
I chose 30 arbitrarily as about as much as you can fit into a Stack Overflow code block without it overflowing and getting a scroll bar; it should be enough to do something interesting without golfing too much, but short enough to keep everyone's attention for a quick demo. For instance, with just a bit of reformatting, the example web server should be able to fit (not counting the data).
Show us your Go code!
This is a web proxy I wrote to provide unauthenticated access to a web service that required HTTP basic auth. I needed it for an internal thingy (and still use it):
package main
import (
"flag"
"log"
"net/http"
"net/url"
)
var target = flag.String("target", "http://www.google.com/", "Where to go.")
var addr = flag.String("listen", ":12345", "Address/port on which to listen.")
var auth = flag.String("auth", "", "Authorization header to add (optional).")
func main() {
flag.Parse()
targetUrl, uerr := url.Parse(*target)
if uerr != nil {
log.Fatalf("Error parsing target ``%s'': ", target, uerr.String())
}
proxy := http.ReverseProxy{Director: func(req *http.Request) {
req.URL.Scheme = targetUrl.Scheme
req.URL.Host = targetUrl.Host
req.Host = targetUrl.Host
if *auth != "" {
req.Header.Set("Authorization", *auth)
}
}}
log.Fatal(http.ListenAndServe(*addr, &proxy))
}
This makes a PNG (on stdout) of a clock face showing the current time. It's barely golfed to fit thirty lines, so the code is not quite as clean as it should be.
package main
import ("image"; "image/png"; "math"; "bufio"; "os"; "time")
const clock_size = 200;
const radius = clock_size / 3;
var colour image.RGBAColor;
func circle (clock *image.RGBA) {
for angle := float64(0); angle < 360; angle++ {
radian_angle := math.Pi * 2 * angle / 360;
x := radius * math.Sin (radian_angle) + clock_size/2;
y := radius * math.Cos (radian_angle) + clock_size/2;
clock.Set (int (x), int (y), colour);}}
func hand (clock *image.RGBA, angle float64, length float64) {
radian_angle := math.Pi * 2 * angle;
x_inc := math.Sin (radian_angle);
y_inc := -math.Cos (radian_angle);
for i := float64(0); i < length; i++ {
x := i * x_inc + clock_size/2;
y := i * y_inc + clock_size/2;
clock.Set (int (x), int (y), colour);}}
func main () {
clock := image.NewRGBA (clock_size, clock_size);
colour.A = 255;
circle (clock);
time := time.LocalTime ();
hand (clock, (float64(time.Hour) + float64(time.Minute)/60)/12, radius*0.6); // hour hand
hand (clock, (float64(time.Minute) + float64(time.Second)/60)/60, radius*0.8); // minute hand
out := bufio.NewWriter(os.Stdout);
defer out.Flush();
png.Encode(out, clock);
}
Run it like
8.out > clock.png
Notice all those float64 casts? I've NEVER seen a language as strict as Go about types.
This is the same code fixed with go fix
(and some manual tweaking) and then automatically formatted using go fmt
. Some newlines where inserted manually.
package main
import (
"bufio"
"image"
"image/color"
"image/png"
"math"
"os"
"time"
)
const clock_size = 200
const radius = clock_size / 3
var colour color.RGBA
func circle(clock *image.RGBA) {
for angle := float64(0); angle < 360; angle++ {
radian_angle := math.Pi * 2 * angle / 360
x := radius*math.Sin(radian_angle) + clock_size/2
y := radius*math.Cos(radian_angle) + clock_size/2
clock.Set(int(x), int(y), colour)
}
}
func hand(clock *image.RGBA, angle float64, length float64) {
radian_angle := math.Pi * 2 * angle
x_inc := math.Sin(radian_angle)
y_inc := -math.Cos(radian_angle)
for i := float64(0); i < length; i++ {
x := i*x_inc + clock_size/2
y := i*y_inc + clock_size/2
clock.Set(int(x), int(y), colour)
}
}
func main() {
clock := image.NewRGBA(image.Rect(0, 0, clock_size, clock_size))
colour.A = 255
circle(clock)
time := time.Now()
hand(clock, (float64(time.Hour())+float64(time.Minute())/60)/12, radius*0.6) // hour hand
hand(clock, (float64(time.Minute())+float64(time.Second())/60)/60, radius*0.8) // minute hand
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
png.Encode(out, clock)
}
OK, I'll get the ball rolling. Here's my first Go program. It's a very primitive chat server, and fits in 30 lines of 80 characters if I compress it down a bit; formatted with gofmt
, it is 60 lines. It listens on a hard coded port (4242), does basically no error handling, and doesn't handle client disconnection other than stopping trying to read from a client if it gets an error.
package main
import ("net";"container/vector";"bufio";"strings")
type client struct { conn net.Conn; send chan string; receive chan string }
func main() {
if listener, err := net.Listen("tcp", "0.0.0.0:4242"); err == nil {
master := make(chan string, 100);
clients := vector.New(0);
go runServer(master, clients);
for {
if conn, err := listener.Accept(); err == nil {
c := client{ conn, master, make(chan string, 100) };
clients.Push(c);
go runClient(c);
} else { break } } } }
func runServer(master chan string, clients *vector.Vector) {
for {
message := <-master;
clients.Do(func (c interface{}) { c.(client).receive <- message }); } }
func runClient(c client) {
input := make(chan string, 10);
go readLines(c, input);
for {
select {
case inMessage := <-input: c.send <- inMessage;
case outMessage := <-c.receive: c.conn.Write(strings.Bytes(outMessage));
} } }
func readLines(c client, input chan string) {
reader := bufio.NewReader(c.conn);
for { if line, err := reader.ReadString('\n'); err == nil
{ input <- line; } else { break } } }
Build and run with:
$ 6g server.go $ 6l -o server server.6 $ ./server
And then in a few other terminals, connect with
$ nc localhost 4242
I really like go's channels and the select
statement, so here's something that shows how easy it is to express the "go and get as many things as possible within a certain time" concept.
This generates as many random numbers as possible within 300 milliseconds and returns the biggest one generated in that time.
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
timeout := time.After(300 * time.Millisecond)
numbers := make(chan int) // This channel will be used
var numberCount int = 0
var maxNumber int = 0
// Start putting random numbers on the numbers channel
go func() {
for {
numbers <- rand.Int()
}
}()
for {
select {
case <- timeout:
fmt.Printf("%v numbers generated. Max number found: %v.\n", numberCount, maxNumber)
return
case number := <- numbers:
numberCount++
if number > maxNumber {
maxNumber = number
}
}
}
}
I copied this from somewhere. Fairly simple, but shows some features.
package main
import ("fmt"; "os" ;"bufio";"io")
func main() {
if len(os.Args) < 2 {
fmt.Printf("usage: catfile \n")
} else if pFile, err := os.OpenFile(os.Args[1], os.O_RDONLY, 0); err != nil {
fmt.Printf("error opening file : %s\n", err)
} else {
defer pFile.Close()
displayFile(pFile)
}
}
func displayFile(pFile *os.File) {
oReader := bufio.NewReader(pFile);
for {
if sLine, err := oReader.ReadString('\n'); err == nil {
fmt.Printf("%s", sLine)
} else {
if err != io.EOF {fmt.Printf("error reading file : %s\n", err)}
break
}
}
}
精彩评论