4
\$\begingroup\$

At the end of the Day 1 Go Course slides (pdf) there is an exercise stated as follows (NOTE: the course in the linked presentation is considered obsolete. If you are looking to learn Go the suggested route is first via http://tour.golang.org):

You all know what the Fibonacci series is. Write a package to implement it. There should be a function to get the next value. (You don't have structs yet; can you find a way to save state without globals?) But instead of addition, make the operation settable by a function provided by the user. Integers? Floats? Strings? Up to you.

How does the following solution rate with respect to its 'Go'-iness?

package fib
type BinOp func (uint64, uint64) uint64
func fib_intern(a, b uint64, op BinOp) uint64 {
 Fib = func(opnew BinOp) uint64 {
 return fib_intern(b, op(a,b), opnew)
 }
 return a
}
var Fib = func(op BinOp) uint64 {
 return fib_intern(0,1,op)
}

Which can then be called like:

package main
import (
 "./fib"
 "fmt"
)
func add(a, b uint64) uint64 {
 return a+b
}
func main() {
 for i := 0; i < 20; i++ {
 fmt.Println(fib.Fib(add))
 }
}
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Sep 16, 2011 at 6:00
\$\endgroup\$
4
  • \$\begingroup\$ Probably the wrong place to ask. Try asking on StackOverflow. \$\endgroup\$ Commented Sep 16, 2011 at 14:46
  • \$\begingroup\$ OK, happy for it to be migrated if its more suitable over in StackOverflow. \$\endgroup\$ Commented Sep 16, 2011 at 23:29
  • \$\begingroup\$ I don't know, it seems to meet the criteria for here. Biggest problem currently is that the link is dead. Presumably the course is obsolete and has been removed from the Go site. \$\endgroup\$ Commented Feb 3, 2013 at 18:45
  • \$\begingroup\$ Thanks for pointing out the broken link. I've updated it to point to an archived version of the course. However, you are correct in that the content is now considered obsolete and the recommended starting point is the Go Tour (tour.golang.org). \$\endgroup\$ Commented Feb 24, 2013 at 12:28

3 Answers 3

6
\$\begingroup\$

The link is dead and the code doesn't compile, but there is still an obvious issue to critique: Part of the challenge was to not save state with "global variables." In Go, package variables of the package main are typically considered "global variables". However, only a single instance of any package ever exists at run time, so package variables of all packages are essentially global variables as well. The code here uses a package variable fib.Fib, thus the code is failing the exercise from the start, working or not.

Edit: Thank you Anthony for tracking down the archived course and updating the question. It helps to have the full context. It also helps to have code working with a current version of Go, which is pretty easy:

package fib
type BinOp func(uint64, uint64) uint64
var Fib func(op BinOp) uint64
func init() {
 Fib = func(op BinOp) uint64 {
 return fib_intern(0, 1, op)
 }
}
func fib_intern(a, b uint64, op BinOp) uint64 {
 Fib = func(opnew BinOp) uint64 {
 return fib_intern(b, op(a, b), opnew)
 }
 return a
}

Go now has stricter checking of static dependencies and reports "initialization loop" with the old code. My update of your code works by moving the initial assignment of Fib to an init function, which runs at program startup.

I'll abandon this answer now and add another answer where I try to stick to your request for a review of the 'Go'-iness.

answered Feb 3, 2013 at 19:06
\$\endgroup\$
0
3
\$\begingroup\$

With working code it's fun to instrument it and watch it work. Each call to fib.Fib computes a new value in the sequence and also replaces itself with a new function literal to hold the new vaue. It's computationally efficient but it does keep allocating and discarding function literals, creating garbage on each call. A better solution would be one that avoided this memory churn. Having a sense for this isn't an idiom, but I think it counts for Go style, to be aware of your use of memory, and to find memory-efficient algorithms.

A purely stylistic issue is the name of the function fib_intern. Go style is to use camel-case as in fibIntern. But even this is redundant. Lower case always means non-exported, so simply 'fib' is enough. There's nothing wrong with having both Fib and fib. Go programmers understand that one is exported, one isn't, and that they are two different things. Perhaps you wanted a more descriptive name than just fib, but fib_intern isn't it. That tells your reader nothing that isn't obvious already.

Finally, the use of a relative import path, "./fib" is okay for a small exercise like this, but it's not a habit you want to form. Relative import paths have historically been problematic, and I think it's fine to do little experiments like this right on your GOPATH.

answered Feb 25, 2013 at 18:19
\$\endgroup\$
0
2
\$\begingroup\$

How about using channels to do it?

func fib(f func(int, int) int) chan int {
 ch := make(chan int)
 a, b := 0, 1
 go func() {
 for {
 a, b = b, f(a, b)
 ch <- a
 }
 }()
 return ch
}

This takes a function which returns an integer given two others, and returns a channel which will supply each term.

You can then run it by doing something like:

func add(a, b int) int {
 return a + b
}
func main() {
 ch := fib(add)
 for i := 0; i < 10; i++ {
 fmt.Println(<-ch)
 }
}

I'm still just getting used to go, so not sure how idiomatic this solution is. You would certainly need to ensure you stopped the goroutine by passing it a 'done' channel or something, but for quick jobs, this is a quick and easy solution.

answered Mar 16, 2015 at 13:06
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.