Go and the 99 Bottles of Beer Song

84 views
Skip to first unread message

Bryan Knowles

unread,
Dec 16, 2009, 4:16:31 PM12/16/09
to golang-nuts
http://99-bottles-of-beer.net/g.html

So I noticed that 99 bottles lacked an entry for go (there's one for
Go!, but on inspection happens to be a separate language).

Opinions on most creative way to approach this song?

Bryan Knowles

unread,
Dec 16, 2009, 9:28:21 PM12/16/09
to golang-nuts

i have the starts of a 99bottles song, uses some pretty simple
channels and
a function that returns two values, for showing off some of the go
idioms
also plan on making it "slur" the lyrics progressively worse as more
beer
becomes consumed.
just need to find the time to be bothered coding it.
(swap random, non-space, char a with random, non-space. unequal-to-a,
char b)

/**
*Ninety-Nine Bottles Song Example
*Author: Bryan Knowles
*Attempts to show Go programming
*idioms while putting a twist
*to the traditional output.
**/

package main

import "fmt"

//Slurs (randomly swaps two chars) string s based on amount a
func slur(s string, a int) string {
/*TODO: "SLUR" THE STRING S BASED ON AMOUNT A*/
return s
}

//Channels the lyrics of the ninety-nine bottles song.
func song(ct int) (chan string, chan bool) {
//Creates channels for piping the song to the main
ch := make(chan string);
qt := make(chan bool);
go func(){
for i := ct; i >= 0; i-- {
//Assigns grammatical fixes
s, n := func ()(string, string){
if i == 1 {return "", ""}
if i == 0 {return "s", "n"}
return "s", ""
}();
//Pushes a true through to keep the main loop running.
qt<-true; ch<-slur(fmt.Sprintf("%v bottle%v of beer on the
wall,", i, s), i);
qt<-true; ch<-slur(fmt.Sprintf("%v bottle%v of beer.", i,
s), i);
qt<-true; ch<-slur(fmt.Sprintf("Take %vone down, pass
%vone around,", n, n), i);
qt<-true; ch<-slur(fmt.Sprintf("%v bottle%v of beer on the
wall.", i, s), i);
qt<-true; ch<-""
}
//Pushes a false through to kill the main loop.
qt<-false
}();
return ch, qt
}

func main(){for line, quit := song(9999); <-quit; fmt.Printf("%v\n", <-
line){}}

SnakE

unread,
Dec 16, 2009, 11:09:04 PM12/16/09
to Bryan Knowles, golang-nuts
2009/12/17 Bryan Knowles <snot...@yahoo.com>

Hey this is fun!  Here's my take.  Behold, highly parallel beer drinking!  XD

package main

import (
        "container/heap"
        "container/vector"
        "fmt"
        "strconv"
        "sync"
)

const stock = 99
const pool = 16

type bottle int

func (b bottle) String() string {
        var count string
        var plural string
        if b == 0 {
                count = "No more"
        } else {
                count = strconv.Itoa(int(b))
        }
        if b != 1 {
                plural = "s"
        }
        return count + " bottle" + plural
}

type verse struct {
        num  bottle
        text string
}

type myHeap struct {
        vector.Vector
}

func (h *myHeap) Less(i, j int) bool { return h.At(i).(verse).num > h.At(j).(verse).num }

func main() {
        bottles := make(chan bottle)
        verses := make(chan verse)
        song := make(chan *myHeap)

        go enumerateBottles(stock, bottles)
        go collectVerses(verses, song)
        for i := 0; i < pool; i++ {
                go buildVerses(bottles, verses)
        }

        var result *myHeap = <-song
        for result.Len() != 0 {
                fmt.Print(heap.Pop(result).(verse).text, "\n\n")
        }
}

func enumerateBottles(max int, bottles chan<- bottle) {
        for i := 0; i <= max; i++ {
                bottles <- bottle(i)
        }
        close(bottles)
}

func collectVerses(verses <-chan verse, song chan<- *myHeap) {
        h := &myHeap{}
        // I'd rather use range here but there is no easy way to detect
        // that nobody is writing to a channel anymore.
        for h.Len() != stock+1 {
                v := <-verses
                heap.Push(h, v)
        }
        song <- h
}

var bottlesLock sync.Mutex

func buildVerses(bottles <-chan bottle, verses chan<- verse) {
        for {
                // This is nasty.  I think this is actually
                // a show-stopper: it's so un-go-ish that I cannot even
                // think about posting this to the 99 bottles site.
                // range MUST work in this case.
                bottlesLock.Lock()
                i := <-bottles
                eob := closed(bottles)
                bottlesLock.Unlock()
                if eob {
                        break
                }

                text := i.String() + " of beer on the wall, " +
                        i.String() + " of beer.\n"
                if i != 0 {
                        text += "Take one down and pass it around, " +
                                (i - 1).String() + " of beer on the wall."
                } else {
                        text += "Go to the store and buy some more, " +
                                bottle(stock).String() +

                                " of beer on the wall."
                }
                verses <- verse{num: i, text: text}
        }
}

Steven Blenkinsop

unread,
Dec 17, 2009, 12:01:54 AM12/17/09
to golang-nuts
I was thinking more something like this:
http://pastebin.com/f30c2c615

Just what I threw together in a half hour. The goroutines are kinda
pointless, since only one is running at a time, but they can only
compute their results after # of bottles has changed :P So,
technically, I could have made them just return their closures and
called those... but I wanted to create the impression of a bunch of
guys in a bar waiting to sing their part.

Message has been deleted

Steven Blenkinsop

unread,
Dec 17, 2009, 3:39:47 AM12/17/09
to golang-nuts
This one is more truly parallel, but is more complex, and parallelism
is still pretty pointless. I'm sure someone with more experience can
make a much more elegant and "Go" solution ;) I'm just having fun and
using it as an excuse to get more familiar with goroutines and
channels:
http://pastebin.com/f216fcab9

Bryan Knowles

unread,
Dec 17, 2009, 10:40:56 AM12/17/09
to golang-nuts

roger peppe

unread,
Dec 17, 2009, 10:58:10 AM12/17/09
to Bryan Knowles, golang-nuts
i did one for limbo a while ago:

http://99-bottles-of-beer.net/language-limbo-1892.html

one could do a similar thing in go, but it would
be nice to include some interface stuff too.

maybe there could be an interface:

type Bottle interface {
BeerLeft() float
Drink(amount float)
}

then some beer drinkers could care what kind of
beer it was:

if b, ok := (<-passitaround).(*Budweiser); ok {
// nope, won't have any of that.
passitaround <- b
}

John Asmuth

unread,
Dec 17, 2009, 11:06:39 AM12/17/09
to golang-nuts
Another concurrent 99 bottles of beer on the wall...

http://gopaste.org/view/R26WR

Bob Cunningham

unread,
Dec 17, 2009, 3:19:11 PM12/17/09
to golang-nuts
I gave this a shot using a very lazy work distribution algorithm. All 99 goroutines share a single request channel and a single reply channel.

http://gopaste.org/view/9E07a

From the header comment:
* Create a shelf containing numbered beer bottles.
* Start tossing out numbers in descending order.
* If a bottle catches a number other than its own, it tosses it back.
* When a bottle catches its own number, it sings, empties itself, then dies.
*
* This is done like a carnival ring-toss, where the number is
* on the ring, and it is checked against the bottle it lands on.
* The bottle gets opened only when the numbers match.


-BobC

Bryan Knowles

unread,
Dec 17, 2009, 9:46:48 PM12/17/09
to golang-nuts
Wow, many songs in a rather short time ;D

I would love that we "voted" on the best song, and
elect the author to submit it to 99-bottles

But I fear the confusion with the existing
language "Go!"

I've even seen a discussion in the issues
started by the creator of Go! over a
suggested name change for Google's Go.

So should we submit it, when we do,
with the name of "Google's Go" ?
Or possibly "Golang" (after the url) ?

SnakE

unread,
Dec 17, 2009, 9:57:08 PM12/17/09
to Bryan Knowles, golang-nuts
2009/12/18 Bryan Knowles <snot...@yahoo.com>

Wow, many songs in a rather short time ;D

I would love that we "voted" on the best song, and
elect the author to submit it to 99-bottles

The site supports many versions of the song for a single language.  My favorites are exception-oriented Java version and a Perl version which looks like four ASCII-art bottles without a single alpha-numeric character inside.
 
But I fear the confusion with the existing
language "Go!"

I think there is even more confusion now because there are two Go languages but only one is present on the site.  I've witnessed one guy getting confused just about half an hour ago.
 
I've even seen a discussion in the issues
started by the creator of Go! over a
suggested name change for Google's Go.

Yes, it was Issue-9.  Sounded familiar.
 
So should we submit it, when we do,
with the name of "Google's Go" ?
Or possibly "Golang" (after the url) ?

I think just "Go" will do.

Bob Cunningham

unread,
Dec 17, 2009, 10:16:42 PM12/17/09
to golang-nuts
Added randomized bottles and verbosity flag: http://gopaste.org/view/SYJpr

-BobC

Bob Cunningham

unread,
Dec 17, 2009, 10:40:19 PM12/17/09
to golang-nuts
Added more stats, minor cleanup: http://gopaste.org/view/MHcVu

I'm done with it. Really. Take me out back and shoot me now.

-BobC

Bob Cunningham

unread,
Dec 18, 2009, 12:49:23 AM12/18/09
to golang-nuts
Mike had some recommendations. Thanks for the code review Mike!

http://gopaste.org/view/MunWk

-BobC

Reply all
Reply to author
Forward
0 new messages