Initial questions from a Go newcomer

408 views
Skip to first unread message

Mark

unread,
Sep 28, 2010, 5:27:47 AM9/28/10
to golang-nuts
Hi,

I've read most of the Go documentation & started to experiment with
Go, but haven't yet found answers to the following questions.

(1) What is the canonical way to test for an empty string? At the
moment I use:

if len(s) > 0 { ... }

but is

if s == "" { ... }

(or something else) better?

(2) What is the canonical way to concatenate arrays? At the moment I
use:

func stringConcatenate(x, y []string) []string {
result := make([]string, len(x) + len(y))
i := 0
for ; i < len(x); i++ {
result[i] = x[i]
}
for j := 0; i < len(x) + len(y); i++ {
result[i] = y[j]
j++
}
return result
}

(3) Why are there no methods for strings? Right now I'm writing things
like:

if strings.HasSuffix(s, ".go") { ... }

whereas

if s.HasSuffix("go") { ... }

seems nicer.

(4) Why is there no support for default arguments?

(5) I use the syntax/go.vim file and that's great; but there's no
indent/go.vim file so vim doesn't handle autoindentation properly
(esp. when you enter a colon). I tried the unofficial one that's on
the Go web site but it doesn't work well.

Thanks!

Jessta

unread,
Sep 28, 2010, 5:56:34 AM9/28/10
to Mark, golang-nuts
On Tue, Sep 28, 2010 at 7:27 PM, Mark <li...@qtrac.plus.com> wrote:
> Hi,
>
> I've read most of the Go documentation & started to experiment with
> Go, but haven't yet found answers to the following questions.
>
> (1) What is the canonical way to test for an empty string? At the
> moment I use:
>
>    if len(s) > 0 { ... }
>
> but is
>
>    if s == "" { ... }
>
> (or something else) better?

Either one is fine, they both generate the same code, it depends on
what you're trying to communicate.

>
> (2) What is the canonical way to concatenate arrays? At the moment I
> use:
>
> func stringConcatenate(x, y []string) []string {
>        result := make([]string, len(x) + len(y))
>        i := 0
>        for ; i < len(x); i++ {
>                result[i] = x[i]
>        }
>        for j := 0; i < len(x) + len(y); i++ {
>                result[i] = y[j]
>                j++
>        }
>        return result
> }

The container/vector package has functions to handle that.
StringVector.InsertVector() is what you want.

> (3) Why are there no methods for strings? Right now I'm writing things
> like:
>
>    if strings.HasSuffix(s, ".go") { ... }
>
> whereas
>
>    if s.HasSuffix("go") { ... }
>
> seems nicer.

None of the base types have methods on them.
This is intentional to prevent issues with interfaces.
i.e base types don't satisfy any interfaces(except for the empty interface)

You can put methods on strings by defining a new type that is a string
and putting methods on that.
eg.
type mystring string;
func (*mystring) HasSuffix(string){}

> (4) Why is there no support for default arguments?

Default arguments make method dispatch complicated and make it easy to
make mistakes when calling methods.
eg. giving the wrong number of arguments to a method and not knowing.

- jessta

--
=====================
http://jessta.id.au

Mark Summerfield

unread,
Sep 28, 2010, 6:37:27 AM9/28/10
to Jessta, golang-nuts
Hi Jessta,

On Tue, 28 Sep 2010 19:56:34 +1000
Jessta <jes...@jessta.id.au> wrote:
> On Tue, Sep 28, 2010 at 7:27 PM, Mark <li...@qtrac.plus.com> wrote:
> > Hi,
> >
> > I've read most of the Go documentation & started to experiment with
> > Go, but haven't yet found answers to the following questions.

[snip]


>
> The container/vector package has functions to handle that.
> StringVector.InsertVector() is what you want.

Thanks. So to concatenate two strings, I'd do:

a := []string{"one", "two", "three"}
b := []string{"four", "five"}
var c vector.StringVector
av := vector.StringVector(a)
bv := vector.StringVector(b)
c.AppendVector(&av)
c.AppendVector(&bv)

Or is there a nicer way?

> > (3) Why are there no methods for strings? Right now I'm writing
> > things like:
> >
> >    if strings.HasSuffix(s, ".go") { ... }
> >
> > whereas
> >
> >    if s.HasSuffix("go") { ... }
> >
> > seems nicer.
>
> None of the base types have methods on them.
> This is intentional to prevent issues with interfaces.
> i.e base types don't satisfy any interfaces(except for the empty
> interface)
>
> You can put methods on strings by defining a new type that is a string
> and putting methods on that.
> eg.
> type mystring string;
> func (*mystring) HasSuffix(string){}

Sure, but is there an extra cost in doing this? And will Go end up with
lots of people creating their own mystrings (rather like the way people
typedef in C to get their own data type aliases)?

> > (4) Why is there no support for default arguments?
>
> Default arguments make method dispatch complicated and make it easy to
> make mistakes when calling methods.
> eg. giving the wrong number of arguments to a method and not knowing.

I'm not convinced by your second reason since Go supports ... arguments.
However, I can see that it would be more complicated to support default
arguments.

Thanks!

--
Mark Summerfield, Qtrac Ltd, www.qtrac.eu
C++, Python, Qt, PyQt - training and consultancy
"Rapid GUI Programming with Python and Qt" - ISBN 0132354187
http://www.qtrac.eu/pyqtbook.html

Peter Bourgon

unread,
Sep 28, 2010, 6:46:16 AM9/28/10
to golang-nuts
On Tue, Sep 28, 2010 at 12:37 PM, Mark Summerfield <li...@qtrac.plus.com> wrote:
> Sure, but is there an extra cost in doing this? And will Go end up with
> lots of people creating their own mystrings (rather like the way people
> typedef in C to get their own data type aliases)?

I've always considered relatively liberal custom typedefing to be a
pretty good C/C++ practice. It's a very thin layer with no
computational cost, and often vastly improves code readability and
maintainability. Is there some seminal "Typedef Considered Harmful"
paper I haven't seen?

chris dollin

unread,
Sep 28, 2010, 6:51:42 AM9/28/10
to Jessta, Mark, golang-nuts
On 28 September 2010 10:56, Jessta <jes...@jessta.id.au> wrote:

> Default arguments make method dispatch complicated

How? I can understand this when methods might be overloaded,
and argument types and/or their presence might make a difference,
but in Go when you see x.f(y) or f(z) you know what method it
is without having to look at y or z (you do need the type of x).
If that method is declared with default arguments, apply those
to the actual argument list. Dispatch doesn't, as far as I can see,
come into it.

> and make it easy to
> make mistakes when calling methods.
> eg. giving the wrong number of arguments to a method and not knowing.

That's what unit tests are for.

(Myself, I find that I can easily be confused when two arguments,
especially adjacent arguments, have the same type and there's
no (or two!) natural orders; I don't get x and y confused, but I can
get row and column confused -- which naturally comes first?)

Chris

--
Chris "allusive" Dollin

Sebastien Binet

unread,
Sep 28, 2010, 7:22:15 AM9/28/10
to chris dollin, Jessta, Mark, golang-nuts

that would probably be something in favor of named arguments (or keyword
arguments):

Frobnicate(row=2, col=3)

or, to better match go's look'n'feel:

Frobnicate(row:2, col:3)

one can already achieve that kind of stuff with structs, but it is a bit
convoluted:

//---
package main

type arg struct {
col int
row int
}

func Frobnicate(a arg) {
println("col:", a.col)
println("row:", a.row)
}

func main() {
Frobnicate(arg{col:2, row:3})
Frobnicate(arg{2,3})
Frobnicate(arg{row:2, col:3})
return
}
/* EOF */

if all this scaffolding could somehow be done for us, that would help
code readability.

cheers,
sebastien.

Andrew Gerrand

unread,
Sep 28, 2010, 8:27:39 AM9/28/10
to Mark, golang-nuts
Hi Mark,

Welcome to the Go community! =D

On 28 September 2010 19:27, Mark <li...@qtrac.plus.com> wrote:
> (1) What is the canonical way to test for an empty string?

Depending on context one way may be clearer, but they're essentially
the same. The idiom used in the Go standard libraries is s == "".

> (2) What is the canonical way to concatenate arrays?

You're on the right track. To concatenate two slices efficiently, you
should use the built-in copy function:

func concat(a, b []string) []string {
c := make([]string, len(a) + len(b))
copy(c[:len(a)], a)
copy(c[len(a):], b)
return c
}

You would also use copy to grow a slice, should it run out of
capacity. (ie, make a new slice and copy the contents of the old one
to the new one)

The vector package is often useful, but never essential. You should
certainly experiment with raw slices and copying to get a feel for
their power and efficiency.

> (3) Why are there no methods for strings? Right now I'm writing things
> like:
>    if strings.HasSuffix(s, ".go") { ... }
> whereas
>    if s.HasSuffix("go") { ... }
> seems nicer.

It seems nicer, sure. But what is the benefit? A small syntactical
bonus, maybe, but it comes with a complexity penalty. Strings are a
primitive type, and methods cannot be defined on primitive types (only
on named, user-defined types).

You could always create your own string type
type String string
and then define a bunch of helper methods on that. You'll find that in
practice it doesn't really matter.

> (4) Why is there no support for default arguments?

It would add complexity to the language, without much gain. If your
function takes enough arguments that defaults become essential, you
might be better off restructuring your code.

The flavour of my last two answers gives you an idea of the design
philosophy behind Go. It is a very simple language. Its features are
easy to understand, and predictable when combined. As a result,
newcomers often wonder why obvious things like this aren't included.
The answer is usually because we thought long and hard about it, and
it just wasn't worth the complexity.

This tends to be Go's most underrated property, as it takes time to
understand its tangible benefits.

> (5) I use the syntax/go.vim file and that's great; but there's no
> indent/go.vim file so vim doesn't handle autoindentation properly
> (esp. when you enter a colon). I tried the unofficial one that's on
> the Go web site but it doesn't work well.

When is indentation necessary after a colon? When you're writing a
struct literal? Eg:

t := MyStruct{
Element: "value",
OtherElement: 12,
}

I use Vim myself, but I haven't felt the need for that particular
feature. This is because Go comes with "gofmt", a command-line program
that reformats Go code with the standard formatting rules. It
automatically aligns and indents everything. After using it for
awhile, one tends to get used to writing lazily and having gofmt clean
it up. (or the reverse, where you get so used to the standard style
that running gofmt is a no-op ;-)

In Vim, you can run gofmt on the current buffer with this command
(without quotes) ":%!gofmt".

Andrew

Mark Summerfield

unread,
Sep 28, 2010, 9:47:54 AM9/28/10
to Andrew Gerrand, golang-nuts
On Tue, 28 Sep 2010 22:27:39 +1000
Andrew Gerrand <a...@golang.org> wrote:
> Hi Mark,
>
> Welcome to the Go community! =D

Thanks:-) { actually I've been lurking a while }



> On 28 September 2010 19:27, Mark <li...@qtrac.plus.com> wrote:
> > (1) What is the canonical way to test for an empty string?
>
> Depending on context one way may be clearer, but they're essentially
> the same. The idiom used in the Go standard libraries is s == "".

Good, that looks nicer.

> > (2) What is the canonical way to concatenate arrays?
>
> You're on the right track. To concatenate two slices efficiently, you
> should use the built-in copy function:
>
> func concat(a, b []string) []string {
> c := make([]string, len(a) + len(b))
> copy(c[:len(a)], a)
> copy(c[len(a):], b)
> return c
> }
>
> You would also use copy to grow a slice, should it run out of
> capacity. (ie, make a new slice and copy the contents of the old one
> to the new one)
>
> The vector package is often useful, but never essential. You should
> certainly experiment with raw slices and copying to get a feel for
> their power and efficiency.

I'm used to Python lists, but in Python you can use + and += on them, so
I wasn't really sure how to do that in Go... but I can see how now that
you've shown me the above.

> > (3) Why are there no methods for strings? Right now I'm writing
> > things like:
> >    if strings.HasSuffix(s, ".go") { ... }
> > whereas
> >    if s.HasSuffix("go") { ... }
> > seems nicer.
>
> It seems nicer, sure. But what is the benefit? A small syntactical
> bonus, maybe, but it comes with a complexity penalty. Strings are a
> primitive type, and methods cannot be defined on primitive types (only
> on named, user-defined types).
>
> You could always create your own string type
> type String string
> and then define a bunch of helper methods on that. You'll find that in
> practice it doesn't really matter.

Since strings will be used an awful lot I suspect that many Go
programmers will end up creating their own (slightly different) String
types purely for that convenience. Since the Go library already has
container/vector for StringVector as a convenience over []string it
seems to me that it would make sense to have say, a strings.String type
as a convenience over string.

> > (4) Why is there no support for default arguments?
>
> It would add complexity to the language, without much gain. If your
> function takes enough arguments that defaults become essential, you
> might be better off restructuring your code.
>
> The flavour of my last two answers gives you an idea of the design
> philosophy behind Go. It is a very simple language. Its features are
> easy to understand, and predictable when combined. As a result,
> newcomers often wonder why obvious things like this aren't included.
> The answer is usually because we thought long and hard about it, and
> it just wasn't worth the complexity.
>
> This tends to be Go's most underrated property, as it takes time to
> understand its tangible benefits.

Sure; I think one consequence will be people using wrappers:

func mycomplexfn(a, b, c, d string) {...}

func mycomplexfn2(a, b string) { mycomplexfn(a, b, "foo", "bar") }

etc.

Which is fine of course.



> > (5) I use the syntax/go.vim file and that's great; but there's no
> > indent/go.vim file so vim doesn't handle autoindentation properly
> > (esp. when you enter a colon). I tried the unofficial one that's on
> > the Go web site but it doesn't work well.
>
> When is indentation necessary after a colon? When you're writing a
> struct literal? Eg:
>
> t := MyStruct{
> Element: "value",
> OtherElement: 12,
> }

This is where I get bitten: I start typing in a new function:

func myfunc(arg string) {
x :
^as soon as I press colon the line gets shifted left (my guess is
that vim thinks it is going to be a label)

so then I complete what I was typing and have to shift right, e.g.
x := baz()

which is irritating.

> I use Vim myself, but I haven't felt the need for that particular
> feature. This is because Go comes with "gofmt", a command-line program
> that reformats Go code with the standard formatting rules. It
> automatically aligns and indents everything. After using it for
> awhile, one tends to get used to writing lazily and having gofmt clean
> it up. (or the reverse, where you get so used to the standard style
> that running gofmt is a no-op ;-)

I'm fine with using gofmt, but it would be nice to have vim indent
correctly as I type---just like it does when I write in any other
lanugage I use.

> In Vim, you can run gofmt on the current buffer with this command
> (without quotes) ":%!gofmt".

Point taken; but still be nicer if vim just did it right in the first
place.

Thanks!

--
Mark Summerfield, Qtrac Ltd, www.qtrac.eu
C++, Python, Qt, PyQt - training and consultancy

"Advanced Qt Programming" - ISBN 0321635906
http://www.qtrac.eu/aqpbook.html

Mark Summerfield

unread,
Sep 28, 2010, 10:54:28 AM9/28/10
to peter....@gmail.com, peterb...@gmail.com, golang-nuts

I doubt it. And I agree with you that typedefs are v. useful esp. for
insulating code from different sized ints etc.

However, heavy use of typedefs can make code much harder to get into,
and for something so fundamental as strings I think it would be a pity
if lots of different Go programmers ended up with their own slightly
different String types.

--
Mark Summerfield, Qtrac Ltd, www.qtrac.eu
C++, Python, Qt, PyQt - training and consultancy

"Programming in Python 3" - ISBN 0321680561
http://www.qtrac.eu/py3book.html

Russ Cox

unread,
Sep 28, 2010, 11:54:15 AM9/28/10
to Jessta, Mark, golang-nuts
>> (1) What is the canonical way to test for an empty string? At the
>> moment I use:
>>
>>    if len(s) > 0 { ... }
>>
>> but is
>>
>>    if s == "" { ... }
>>
>> (or something else) better?
>
> Either one is fine, they both generate the same code, it depends on
> what you're trying to communicate.

And there is always s + "badgerbadgerbadger" == "badgerbadgerbadger".
http://groups.google.com/group/golang-nuts/msg/2e321a2e6db42ead

Russ

Andrew Gerrand

unread,
Sep 28, 2010, 7:34:40 PM9/28/10
to Mark Summerfield, golang-nuts
On 28 September 2010 23:47, Mark Summerfield <li...@qtrac.plus.com> wrote:
> I'm used to Python lists, but in Python you can use + and += on them, so
> I wasn't really sure how to do that in Go... but I can see how now that
> you've shown me the above.

As you can see, Go requires you to be explicit when making
allocations. The vector package provides support for Python-like
managed lists.

> Since strings will be used an awful lot I suspect that many Go
> programmers will end up creating their own (slightly different) String
> types purely for that convenience.

That's not been my observation so far. It's way easier to just type
"strings." than to implement the wrapper (and convert to and from the
named type when interfacing with other code). Only if you were doing
custom string processing would it really pay off.

> Since the Go library already has
> container/vector for StringVector as a convenience over []string it
> seems to me that it would make sense to have say, a strings.String type
> as a convenience over string.

I use both in different settings. More often than not, all you want is
a plain old slice.

> This is where I get bitten: I start typing in a new function:
>
> func myfunc(arg string) {
>    x :
>       ^as soon as I press colon the line gets shifted left (my guess is
>       that vim thinks it is going to be a label)

That does indeed sound broken. Do you know much about Vim's
smartindent configuration? (I don't.) If you do, would you please fix
it and submit a patch? (or maybe other Vim wizards that are reading
can help...)
http://golang.org/doc/contribute.html

> Point taken; but still be nicer if vim just did it right in the first
> place.

For sure.

Cheers,
Andrew

Mark Summerfield

unread,
Sep 29, 2010, 3:29:58 AM9/29/10
to Andrew Gerrand, golang-nuts
Hi Andrew,

On Wed, 29 Sep 2010 09:34:40 +1000
Andrew Gerrand <a...@golang.org> wrote:
[snip]


> > This is where I get bitten: I start typing in a new function:
> >
> > func myfunc(arg string) {
> >    x :
> >       ^as soon as I press colon the line gets shifted left (my
> > guess is that vim thinks it is going to be a label)
>
> That does indeed sound broken. Do you know much about Vim's
> smartindent configuration? (I don't.) If you do, would you please fix
> it and submit a patch? (or maybe other Vim wizards that are reading
> can help...)
> http://golang.org/doc/contribute.html

[snip]

I don't know enough about programming vim unfortunately. Also, it is
even more broken because even after you shift right when you hit Enter
to go to the next line it is wrongly indented.

So I hope a Vim guru will help here!

Andrew Gerrand

unread,
Sep 29, 2010, 3:44:24 AM9/29/10
to Mark Summerfield, golang-nuts
On 29 September 2010 17:29, Mark Summerfield <li...@qtrac.plus.com> wrote:
> I don't know enough about programming vim unfortunately. Also, it is
> even more broken because even after you shift right when you hit Enter
> to go to the next line it is wrongly indented.
>
> So I hope a Vim guru will help here!

A temporary fix might be to turn smartindent off with ":set nosmartindent"

Andrew

Mark Summerfield

unread,
Sep 29, 2010, 3:54:02 AM9/29/10
to golang-nuts
Hi,

As far as I can tell Go has no explicit support for static data in
functions---or have I just missed seeing it?

My use cases are (1) when I have pure functions that contain expensive
computations and I want to keep a local cache of results inside them and
(2) when I have a lot of data in the function that I don't want to
recreate on every call.

At the moment I'm solving this by using closures along these lines:

var MyFunc func(x)y = makeMyFunc()

func makeMyFunc() func(x)y
staticData := ...
return func(x)y {
... // uses the staticData
}
}

Is this the correct idiom for static data inside functions in Go?

Thanks!

PS I've finished my first Go program < 200 lines called "onego". Here's
its -help:

-------
usage: onego single_file_program1.go [single_file_program2.go [...]]
-or- onego single_file_program1.go [arg1 [arg2 [...]]]

If multiple files are given they are all built; if one file is given it
is built and run and given any arguments that are passed.
-------

I've found this v. useful for creating lots of small single-file test
programs in the same directory to help me learn Go since to build & run
each time I just do:

$ onego myprog.go

without needing to bother creating makefiles.

If this would be of any use to anyone else I'll happily send it to
you---it is a single .go file of course:-)


--
Mark Summerfield, Qtrac Ltd, www.qtrac.eu
C++, Python, Qt, PyQt - training and consultancy

chris dollin

unread,
Sep 29, 2010, 3:59:24 AM9/29/10
to Mark Summerfield, golang-nuts
On 29 September 2010 08:54, Mark Summerfield <li...@qtrac.plus.com> wrote:
> Hi,
>
> As far as I can tell Go has no explicit support for static data in
> functions---or have I just missed seeing it?
>
> My use cases are (1) when I have pure functions that contain expensive
> computations and I want to keep a local cache of results inside them and
> (2) when I have a lot of data in the function that I don't want to
> recreate on every call.
>
> At the moment I'm solving this by using closures along these lines:
>
> var MyFunc func(x)y = makeMyFunc()
>
> func makeMyFunc() func(x)y
>    staticData := ...
>    return func(x)y {
>        ... // uses the staticData
>    }
> }
>
> Is this the correct idiom for static data inside functions in Go?

I would just use an unexported package-scope variable and mutter behind my
teeth about scope versus extent.

Mark Summerfield

unread,
Sep 29, 2010, 5:52:24 AM9/29/10
to Andrew Gerrand, golang-nuts
Hi Andrew,

On Wed, 29 Sep 2010 18:00:49 +1000
Andrew Gerrand <a...@golang.org> wrote:
> On 29 September 2010 17:54, Mark Summerfield <li...@qtrac.plus.com>


> wrote:
> > Hi,
> >
> > As far as I can tell Go has no explicit support for static data in
> > functions---or have I just missed seeing it?
> >
> > My use cases are (1) when I have pure functions that contain
> > expensive computations and I want to keep a local cache of results
> > inside them and (2) when I have a lot of data in the function that
> > I don't want to recreate on every call.
> >
> > At the moment I'm solving this by using closures along these lines:

[snip]


> > Is this the correct idiom for static data inside functions in Go?
>

> The more idiomatic way:
>
> --
>
> package foo
>
> var staticData = preCalc()
>
> func preCalc() int {
> // do something expensive
> return // result
> }
>
>
> func Bar() {
> // use staticData
> }
>
> --
>
> Both preCalc and staticData are package-private; only Bar is visible
> outside.

Yes, I love Go's really nice approach to private/Public.

> Also, check out sync.Once; that will allow you to only do the
> expensive initialisation when Bar() is called for the first time.

I'm still working my way through the pkg docs!

Your idiom is fine for use case (2), but that doesn't help use case (1),
i.e., if you want a local cache that's populated on the fly.

For example: if you're doing lots of string slicing (by character) and
are mostly using the same strings. This involves converting string to
[]int and back all the time. So I came up with this:


var RunesForString func(string)([]int) = makeRunesForStringFunction()
var MaxRunesCacheSize int = 255

func makeRunesForStringFunction() func(string)([]int) {
cache := make(map[string][]int)
return func(s string) (runes []int) {
runes, ok := cache[s]
if !ok {
runes = []int(s)
cache[s] = runes
}
if len(cache) > MaxRunesCacheSize {
cache = make(map[string][]int)
}
return
}
}

I'm happy with this, but want to be sure that I'm doing things in the
most idomatically correct way.

Thanks!

--
Mark Summerfield, Qtrac Ltd, www.qtrac.eu
C++, Python, Qt, PyQt - training and consultancy

Solar Granulation

unread,
Sep 29, 2010, 6:01:35 AM9/29/10
to golang-nuts


On Sep 29, 8:54 am, Mark Summerfield <l...@qtrac.plus.com> wrote:
> Hi,
>
> As far as I can tell Go has no explicit support for static data in
> functions---or have I just missed seeing it?
>
> My use cases are (1) when I have pure functions that contain expensive
> computations and I want to keep a local cache of results inside them and
> (2) when I have a lot of data in the function that I don't want to
> recreate on every call.
>
> At the moment I'm solving this by using closures along these lines:
>
> var MyFunc func(x)y = makeMyFunc()
>
> func makeMyFunc() func(x)y
>     staticData := ...
>     return func(x)y {
>         ... // uses the staticData
>     }
>
> }

I'm not yet experienced enough to speak to Go idioms, but I'd like to
contribute here. First I would say that you could, as Chris
mentioned, use unexported package-level variables. But I also have
another suggestion, because I'm like that. You may feel that what I'm
going to suggest would be too convoluted or otherwise suboptimal, it's
just an idea :)

The idea revolves around goroutines and channels. First though, if
your function takes multiple arguments, I'd suggest creating a struct
to hold all the appropriate values for a call to that function. Write
the function to take a channel of that struct type, and another
channel of appropriate type for the return value. Launch the function
as a goroutine, so it can listen for incoming data and process it.

type argStruct struct {
a, b string
c int
}

func theFunc(in chan argStruct, out chan string) {
staticData := // Whatever it needs to be
for {
newArgs := <- in
// Do stuff with the values
out <- "result" // Obviously not literally
}
}

Then create your channels and do "go theFunc(ch1, ch2)"

Andrew Gerrand

unread,
Sep 29, 2010, 6:29:38 AM9/29/10
to Mark Summerfield, golang-nuts
On 29 September 2010 19:52, Mark Summerfield <li...@qtrac.plus.com> wrote:
> For example: if you're doing lots of string slicing (by character) and
> are mostly using the same strings.
> I'm happy with this, but want to be sure that I'm doing things in the
> most idomatically correct way.

Just create a type which holds that data, and methods that act on it.
This will also let you put a mutex in there to guard the map from
being corrupted by concurrent access.

--

package main

import (
"fmt"
"sync"
)

type RuneCache struct {
cache map[string][]int
mu sync.Mutex
}

func NewRuneCache() *RuneCache {
return &RuneCache{cache: make(map[string][]int)}
}

func (rc *RuneCache) Runes(s string) []int {
rc.mu.Lock()
defer rc.mu.Unlock()
runes, ok := rc.cache[s]


if !ok {
runes = []int(s)

rc.cache[s] = runes
}
return runes
}

func main() {
rc := NewRuneCache()
fmt.Println(rc.Runes("abcd"))
}

--

I think this is a lot clearer than using the closure, plus it lets you
add functionality more easily.

As an aside, one thing that might be of relevance to you is
utf8.String (available in this week's forthcoming release), which
permits reasonably efficient indexing of strings by rune. Depending on
your use case, it could offer significant efficiency benefits over
converting each string to []int.

Andrew

roger peppe

unread,
Sep 29, 2010, 7:01:16 AM9/29/10
to Mark Summerfield, Andrew Gerrand, golang-nuts
On 29 September 2010 10:52, Mark Summerfield <li...@qtrac.plus.com> wrote:
> var RunesForString func(string)([]int) = makeRunesForStringFunction()
> var MaxRunesCacheSize int = 255
>
> func makeRunesForStringFunction() func(string)([]int) {
>        cache := make(map[string][]int)
>        return func(s string) (runes []int) {
>                runes, ok := cache[s]
>                if !ok {
>                        runes = []int(s)
>                        cache[s] = runes
>                }
>                if len(cache) > MaxRunesCacheSize {
>                        cache = make(map[string][]int)
>                }
>                return
>        }
> }
>
> I'm happy with this, but want to be sure that I'm doing things in the
> most idomatically correct way.

as far as i can see, the above code is functionally identical
to this, which is more conventional:

var cache = make(map[string][]int)
func RunesForString(s string) []int {
const MaxRunesCacheSize = 255


runes, ok := cache[s]
if !ok {
runes = []int(s)
cache[s] = runes
}
if len(cache) > MaxRunesCacheSize {
cache = make(map[string][]int)
}

return runes
}

i can see that in some cases, you might want to have
some persistent state for a function that isn't global
(for instance, say you wanted the ability to have
several different caches of different maximum sizes)

in that case you could go with the way you were using,
but it would probably be more idiomatic to define the
function as a method on a type that holds the data.
this has the advantage is that it's easier to debug - you
can easily print out the cache contents if needed.

for instance:

type Cache struct {
maxSize int
cache map[string][]int
}

func (c *Cache) RunesForString(s string) []int {
...
}

of course with all these methods, you have to be careful about
concurrent access - it's worth wrapping the map access in a sync.Mutex.

Russ Cox

unread,
Sep 29, 2010, 7:54:14 AM9/29/10
to roger peppe, Mark Summerfield, Andrew Gerrand, golang-nuts
> of course with all these methods, you have to be careful about
> concurrent access - it's worth wrapping the map access in a sync.Mutex.

or if it's really one time, use sync.Once.

russ

Mark Summerfield

unread,
Sep 29, 2010, 9:45:41 AM9/29/10
to Andrew Gerrand, golang-nuts
Hi Andrew,

On Wed, 29 Sep 2010 20:29:38 +1000
Andrew Gerrand <a...@golang.org> wrote:
> On 29 September 2010 19:52, Mark Summerfield <li...@qtrac.plus.com>
> wrote:
> > For example: if you're doing lots of string slicing (by character)
> > and are mostly using the same strings.
> > I'm happy with this, but want to be sure that I'm doing things in
> > the most idomatically correct way.
>
> Just create a type which holds that data, and methods that act on it.
> This will also let you put a mutex in there to guard the map from
> being corrupted by concurrent access.

Thanks for this---and thanks to to Roger & Russ for their ideas.

I clearly also need to spend some time reading the pkg docs:-)

[snip]

> As an aside, one thing that might be of relevance to you is
> utf8.String (available in this week's forthcoming release), which
> permits reasonably efficient indexing of strings by rune. Depending on
> your use case, it could offer significant efficiency benefits over
> converting each string to []int.

I look forward to that!


--
Mark Summerfield, Qtrac Ltd, www.qtrac.eu
C++, Python, Qt, PyQt - training and consultancy

Reply all
Reply to author
Forward
0 new messages