mirror of
https://github.com/golang/go
synced 2024-11-22 01:04:40 -07:00
fixups in the tutorial
also add a section about compiling programs because some found it surprising not to have one R=rsc CC=go-dev http://go/go-review/1018011
This commit is contained in:
parent
dde666dab5
commit
83f0b718ed
@ -1,12 +1,15 @@
|
|||||||
<!-- Let's Go -->
|
<!-- Let's Go -->
|
||||||
<h2>Introduction</h2>
|
<h2>Introduction</h2>
|
||||||
<p>
|
<p>
|
||||||
This document is a tutorial introduction to the basics of the Go systems programming
|
This document is a tutorial introduction to the basics of the Go programming
|
||||||
language, intended for programmers familiar with C or C++. It is not a comprehensive
|
language, intended for programmers familiar with C or C++. It is not a comprehensive
|
||||||
guide to the language; at the moment the document closest to that is the
|
guide to the language; at the moment the document closest to that is the
|
||||||
<a href='/doc/go_spec.html'>language specification.</a>
|
<a href='/doc/go_spec.html'>language specification</a>.
|
||||||
|
After you've read this tutorial, you might want to look at
|
||||||
|
<a href='/doc/effective_go.html'>Effective Go</a>,
|
||||||
|
which digs deeper into how the language is used.
|
||||||
<p>
|
<p>
|
||||||
The presentation proceeds through a series of modest programs to illustrate
|
The presentation here proceeds through a series of modest programs to illustrate
|
||||||
key features of the language. All the programs work (at time of writing) and are
|
key features of the language. All the programs work (at time of writing) and are
|
||||||
checked into the repository in the directory <a href='/doc/progs'><code>/doc/progs/</code></a>.
|
checked into the repository in the directory <a href='/doc/progs'><code>/doc/progs/</code></a>.
|
||||||
<p>
|
<p>
|
||||||
@ -48,6 +51,35 @@ The comment convention is the same as in C++:
|
|||||||
<p>
|
<p>
|
||||||
Later we'll have much more to say about printing.
|
Later we'll have much more to say about printing.
|
||||||
<p>
|
<p>
|
||||||
|
<h2>Compiling</h2>
|
||||||
|
<p>
|
||||||
|
Go is a compiled language. At the moment there are two compilers.
|
||||||
|
<code>Gccgo</code> is a Go compiler that uses the GCC back end. There is also a
|
||||||
|
suite of compilers with different (and odd) names for each architecture:
|
||||||
|
<code>6g</code> for the 64-bit x86, <code>8g</code> for the 32-bit x86, and more. These
|
||||||
|
compilers run significantly faster but generate less efficient code
|
||||||
|
than <code>gccgo</code>. At the time of writing (late 2009), they also have
|
||||||
|
a more robust run-time system although <code>gccgo</code> is catching up.
|
||||||
|
<p>
|
||||||
|
Here's how to compile and run our program. With <code>6g</code>, say,
|
||||||
|
<p>
|
||||||
|
<pre>
|
||||||
|
$ 6g helloworld.go # compile; object goes into helloworld.6
|
||||||
|
$ 6l helloworld.6 # link; output goes into 6.out
|
||||||
|
$ 6.out
|
||||||
|
Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
|
||||||
|
$
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
With <code>gccgo</code> it looks a little more traditional.
|
||||||
|
<p>
|
||||||
|
<pre>
|
||||||
|
$ gccgo helloworld.go
|
||||||
|
$ a.out
|
||||||
|
Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
|
||||||
|
$
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
<h2>Echo</h2>
|
<h2>Echo</h2>
|
||||||
<p>
|
<p>
|
||||||
Next up, here's a version of the Unix utility <code>echo(1)</code>:
|
Next up, here's a version of the Unix utility <code>echo(1)</code>:
|
||||||
@ -63,8 +95,8 @@ Next up, here's a version of the Unix utility <code>echo(1)</code>:
|
|||||||
12 var n_flag = flag.Bool("n", false, "don't print final newline")
|
12 var n_flag = flag.Bool("n", false, "don't print final newline")
|
||||||
<p>
|
<p>
|
||||||
14 const (
|
14 const (
|
||||||
15 kSpace = " ";
|
15 Space = " ";
|
||||||
16 kNewline = "\n";
|
16 Newline = "\n";
|
||||||
17 )
|
17 )
|
||||||
<p>
|
<p>
|
||||||
19 func main() {
|
19 func main() {
|
||||||
@ -72,22 +104,22 @@ Next up, here's a version of the Unix utility <code>echo(1)</code>:
|
|||||||
21 var s string = "";
|
21 var s string = "";
|
||||||
22 for i := 0; i < flag.NArg(); i++ {
|
22 for i := 0; i < flag.NArg(); i++ {
|
||||||
23 if i > 0 {
|
23 if i > 0 {
|
||||||
24 s += kSpace
|
24 s += Space
|
||||||
25 }
|
25 }
|
||||||
26 s += flag.Arg(i)
|
26 s += flag.Arg(i)
|
||||||
27 }
|
27 }
|
||||||
28 if !*n_flag {
|
28 if !*n_flag {
|
||||||
29 s += kNewline
|
29 s += Newline
|
||||||
30 }
|
30 }
|
||||||
31 os.Stdout.WriteString(s);
|
31 os.Stdout.WriteString(s);
|
||||||
32 }
|
32 }
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
This program is small but it's doing a number of new things. In the last example,
|
This program is small but it's doing a number of new things. In the last example,
|
||||||
we saw <code>func</code> introducing a function. The keywords <code>var</code>, <code>const</code>, and <code>type</code>
|
we saw <code>func</code> introduce a function. The keywords <code>var</code>, <code>const</code>, and <code>type</code>
|
||||||
(not used yet) also introduce declarations, as does <code>import</code>.
|
(not used yet) also introduce declarations, as does <code>import</code>.
|
||||||
Notice that we can group declarations of the same sort into
|
Notice that we can group declarations of the same sort into
|
||||||
parenthesized, semicolon-separated lists if we want, as on lines 4-10 and 14-17.
|
parenthesized, semicolon-separated lists if we want, as on lines 7-10 and 14-17.
|
||||||
But it's not necessary to do so; we could have said
|
But it's not necessary to do so; we could have said
|
||||||
<p>
|
<p>
|
||||||
<pre>
|
<pre>
|
||||||
@ -168,7 +200,7 @@ Later examples will show some other ways <code>for</code> can be written.
|
|||||||
<p>
|
<p>
|
||||||
The body of the loop builds up the string <code>s</code> by appending (using <code>+=</code>)
|
The body of the loop builds up the string <code>s</code> by appending (using <code>+=</code>)
|
||||||
the flags and separating spaces. After the loop, if the <code>-n</code> flag is not
|
the flags and separating spaces. After the loop, if the <code>-n</code> flag is not
|
||||||
set, it appends a newline, and then writes the result.
|
set, the program appends a newline. Finally, it writes the result.
|
||||||
<p>
|
<p>
|
||||||
Notice that <code>main.main</code> is a niladic function with no return type.
|
Notice that <code>main.main</code> is a niladic function with no return type.
|
||||||
It's defined that way. Falling off the end of <code>main.main</code> means
|
It's defined that way. Falling off the end of <code>main.main</code> means
|
||||||
@ -234,7 +266,7 @@ about pointers to arrays.
|
|||||||
The size of the array is part of its type; however, one can declare
|
The size of the array is part of its type; however, one can declare
|
||||||
a <i>slice</i> variable, to which one can assign a pointer to
|
a <i>slice</i> variable, to which one can assign a pointer to
|
||||||
any array
|
any array
|
||||||
with the same element type or - much more commonly - a <i>slice
|
with the same element type or—much more commonly—a <i>slice
|
||||||
expression</i> of the form <code>a[low : high]</code>, representing
|
expression</i> of the form <code>a[low : high]</code>, representing
|
||||||
the subarray indexed by <code>low</code> through <code>high-1</code>.
|
the subarray indexed by <code>low</code> through <code>high-1</code>.
|
||||||
Slices look a lot like arrays but have
|
Slices look a lot like arrays but have
|
||||||
@ -276,7 +308,7 @@ and invoke it like this:
|
|||||||
Note how the return type (<code>int</code>) is defined for <code>sum()</code> by stating it
|
Note how the return type (<code>int</code>) is defined for <code>sum()</code> by stating it
|
||||||
after the parameter list.
|
after the parameter list.
|
||||||
The expression <code>[3]int{1,2,3}</code> -- a type followed by a brace-bounded expression
|
The expression <code>[3]int{1,2,3}</code> -- a type followed by a brace-bounded expression
|
||||||
-- is a constructor for a value, in this case an array of 3 <code>ints</code>. Putting an <code>&</code>
|
-- is a constructor for a value, in this case an array of 3 <code>ints</code>. Putting an <code>&</code>
|
||||||
in front gives us the address of a unique instance of the value. We pass the
|
in front gives us the address of a unique instance of the value. We pass the
|
||||||
pointer to <code>sum()</code> by (automatically) promoting it to a slice.
|
pointer to <code>sum()</code> by (automatically) promoting it to a slice.
|
||||||
<p>
|
<p>
|
||||||
@ -288,7 +320,7 @@ elements for you, use <code>...</code> as the array size:
|
|||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
In practice, though, unless you're meticulous about storage layout within a
|
In practice, though, unless you're meticulous about storage layout within a
|
||||||
data structure, a slice itself - using empty brackets and no <code>&</code> - is all you need:
|
data structure, a slice itself - using empty brackets and no <code>&</code> - is all you need:
|
||||||
<p>
|
<p>
|
||||||
<pre>
|
<pre>
|
||||||
s := sum([]int{1,2,3});
|
s := sum([]int{1,2,3});
|
||||||
@ -297,7 +329,7 @@ data structure, a slice itself - using empty brackets and no <code>&</code> - is
|
|||||||
There are also maps, which you can initialize like this:
|
There are also maps, which you can initialize like this:
|
||||||
<p>
|
<p>
|
||||||
<pre>
|
<pre>
|
||||||
m := map[string] int {"one":1 , "two":2}
|
m := map[string]int{"one":1 , "two":2}
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
The built-in function <code>len()</code>, which returns number of elements,
|
The built-in function <code>len()</code>, which returns number of elements,
|
||||||
@ -330,14 +362,14 @@ referencing the same underlying data will see the modification. For these three
|
|||||||
types you want to use the built-in function <code>make()</code>:
|
types you want to use the built-in function <code>make()</code>:
|
||||||
<p>
|
<p>
|
||||||
<pre>
|
<pre>
|
||||||
m := make(map[string] int);
|
m := make(map[string]int);
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
This statement initializes a new map ready to store entries.
|
This statement initializes a new map ready to store entries.
|
||||||
If you just declare the map, as in
|
If you just declare the map, as in
|
||||||
<p>
|
<p>
|
||||||
<pre>
|
<pre>
|
||||||
var m map[string] int;
|
var m map[string]int;
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
it creates a <code>nil</code> reference that cannot hold anything. To use the map,
|
it creates a <code>nil</code> reference that cannot hold anything. To use the map,
|
||||||
@ -352,7 +384,7 @@ declaring an uninitialized variable and taking its address.
|
|||||||
<h2>An Interlude about Constants</h2>
|
<h2>An Interlude about Constants</h2>
|
||||||
<p>
|
<p>
|
||||||
Although integers come in lots of sizes in Go, integer constants do not.
|
Although integers come in lots of sizes in Go, integer constants do not.
|
||||||
There are no constants like <code>0ll</code> or <code>0x0UL</code>. Instead, integer
|
There are no constants like <code>0LL</code> or <code>0x0UL</code>. Instead, integer
|
||||||
constants are evaluated as large-precision values that
|
constants are evaluated as large-precision values that
|
||||||
can overflow only when they are assigned to an integer variable with
|
can overflow only when they are assigned to an integer variable with
|
||||||
too little precision to represent the value.
|
too little precision to represent the value.
|
||||||
@ -394,7 +426,7 @@ sort of open/close/read/write interface. Here's the start of <code>file.go</cod
|
|||||||
10 )
|
10 )
|
||||||
<p>
|
<p>
|
||||||
12 type File struct {
|
12 type File struct {
|
||||||
13 fd int; // file descriptor number
|
13 fd int; // file descriptor number
|
||||||
14 name string; // file name at Open time
|
14 name string; // file name at Open time
|
||||||
15 }
|
15 }
|
||||||
</pre>
|
</pre>
|
||||||
@ -415,8 +447,8 @@ that the file descriptor refers to.
|
|||||||
<p>
|
<p>
|
||||||
Because <code>File</code> starts with a capital letter, the type is available outside the package,
|
Because <code>File</code> starts with a capital letter, the type is available outside the package,
|
||||||
that is, by users of the package. In Go the rule about visibility of information is
|
that is, by users of the package. In Go the rule about visibility of information is
|
||||||
simple: if a name (of a top-level type, function, method, constant, variable, or of
|
simple: if a name (of a top-level type, function, method, constant or variable, or of
|
||||||
a structure field) is capitalized, users of the package may see it. Otherwise, the
|
a structure field or method) is capitalized, users of the package may see it. Otherwise, the
|
||||||
name and hence the thing being named is visible only inside the package in which
|
name and hence the thing being named is visible only inside the package in which
|
||||||
it is declared. This is more than a convention; the rule is enforced by the compiler.
|
it is declared. This is more than a convention; the rule is enforced by the compiler.
|
||||||
In Go, the term for publicly visible names is ''exported''.
|
In Go, the term for publicly visible names is ''exported''.
|
||||||
@ -545,7 +577,7 @@ the <code>struct</code> declaration itself. The <code>struct</code> declaration
|
|||||||
In fact, methods can be created for any type you name, such as an integer or
|
In fact, methods can be created for any type you name, such as an integer or
|
||||||
array, not just for <code>structs</code>. We'll see an example with arrays later.
|
array, not just for <code>structs</code>. We'll see an example with arrays later.
|
||||||
<p>
|
<p>
|
||||||
The <code>String</code> method is so called because of printing convention we'll
|
The <code>String</code> method is so called because of a printing convention we'll
|
||||||
describe later.
|
describe later.
|
||||||
<p>
|
<p>
|
||||||
The methods use the public variable <code>os.EINVAL</code> to return the (<code>os.Error</code>
|
The methods use the public variable <code>os.EINVAL</code> to return the (<code>os.Error</code>
|
||||||
@ -607,13 +639,13 @@ Building on the <code>file</code> package, here's a simple version of the Unix u
|
|||||||
17 for {
|
17 for {
|
||||||
18 switch nr, er := f.Read(&buf); true {
|
18 switch nr, er := f.Read(&buf); true {
|
||||||
19 case nr < 0:
|
19 case nr < 0:
|
||||||
20 fmt.Fprintf(os.Stderr, "error reading from %s: %s\n", f.String(), er.String());
|
20 fmt.Fprintf(os.Stderr, "cat: error reading from %s: %s\n", f.String(), er.String());
|
||||||
21 os.Exit(1);
|
21 os.Exit(1);
|
||||||
22 case nr == 0: // EOF
|
22 case nr == 0: // EOF
|
||||||
23 return;
|
23 return;
|
||||||
24 case nr > 0:
|
24 case nr > 0:
|
||||||
25 if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr {
|
25 if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr {
|
||||||
26 fmt.Fprintf(os.Stderr, "error writing from %s: %s\n", f.String(), ew.String());
|
26 fmt.Fprintf(os.Stderr, "cat: error writing from %s: %s\n", f.String(), ew.String());
|
||||||
27 }
|
27 }
|
||||||
28 }
|
28 }
|
||||||
29 }
|
29 }
|
||||||
@ -627,7 +659,7 @@ Building on the <code>file</code> package, here's a simple version of the Unix u
|
|||||||
37 for i := 0; i < flag.NArg(); i++ {
|
37 for i := 0; i < flag.NArg(); i++ {
|
||||||
38 f, err := file.Open(flag.Arg(i), 0, 0);
|
38 f, err := file.Open(flag.Arg(i), 0, 0);
|
||||||
39 if f == nil {
|
39 if f == nil {
|
||||||
40 fmt.Fprintf(os.Stderr, "can't open %s: error %s\n", flag.Arg(i), err);
|
40 fmt.Fprintf(os.Stderr, "cat: can't open %s: error %s\n", flag.Arg(i), err);
|
||||||
41 os.Exit(1);
|
41 os.Exit(1);
|
||||||
42 }
|
42 }
|
||||||
43 cat(f);
|
43 cat(f);
|
||||||
@ -702,7 +734,7 @@ we have a second implementation of the <code>reader</code> interface.
|
|||||||
50 // end of rotate13 implementation
|
50 // end of rotate13 implementation
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
(The <code>rot13</code> function called on line 42 is trivial and not worth reproducing.)
|
(The <code>rot13</code> function called on line 42 is trivial and not worth reproducing here.)
|
||||||
<p>
|
<p>
|
||||||
To use the new feature, we define a flag:
|
To use the new feature, we define a flag:
|
||||||
<p>
|
<p>
|
||||||
@ -723,14 +755,14 @@ and use it from within a mostly unchanged <code>cat()</code> function:
|
|||||||
59 for {
|
59 for {
|
||||||
60 switch nr, er := r.Read(&buf); {
|
60 switch nr, er := r.Read(&buf); {
|
||||||
61 case nr < 0:
|
61 case nr < 0:
|
||||||
62 fmt.Fprintf(os.Stderr, "error reading from %s: %s\n", r.String(), er.String());
|
62 fmt.Fprintf(os.Stderr, "cat: error reading from %s: %s\n", r.String(), er.String());
|
||||||
63 os.Exit(1);
|
63 os.Exit(1);
|
||||||
64 case nr == 0: // EOF
|
64 case nr == 0: // EOF
|
||||||
65 return;
|
65 return;
|
||||||
66 case nr > 0:
|
66 case nr > 0:
|
||||||
67 nw, ew := file.Stdout.Write(buf[0:nr]);
|
67 nw, ew := file.Stdout.Write(buf[0:nr]);
|
||||||
68 if nw != nr {
|
68 if nw != nr {
|
||||||
69 fmt.Fprintf(os.Stderr, "error writing from %s: %s\n", r.String(), ew.String());
|
69 fmt.Fprintf(os.Stderr, "cat: error writing from %s: %s\n", r.String(), ew.String());
|
||||||
70 }
|
70 }
|
||||||
71 }
|
71 }
|
||||||
72 }
|
72 }
|
||||||
@ -739,7 +771,7 @@ and use it from within a mostly unchanged <code>cat()</code> function:
|
|||||||
<p>
|
<p>
|
||||||
(We could also do the wrapping in <code>main</code> and leave <code>cat()</code> mostly alone, except
|
(We could also do the wrapping in <code>main</code> and leave <code>cat()</code> mostly alone, except
|
||||||
for changing the type of the argument; consider that an exercise.)
|
for changing the type of the argument; consider that an exercise.)
|
||||||
Lines 56 through 59 set it all up: If the <code>rot13</code> flag is true, wrap the <code>reader</code>
|
Lines 56 through 58 set it all up: If the <code>rot13</code> flag is true, wrap the <code>reader</code>
|
||||||
we received into a <code>rotate13</code> and proceed. Note that the interface variables
|
we received into a <code>rotate13</code> and proceed. Note that the interface variables
|
||||||
are values, not pointers: the argument is of type <code>reader</code>, not <code>*reader</code>,
|
are values, not pointers: the argument is of type <code>reader</code>, not <code>*reader</code>,
|
||||||
even though under the covers it holds a pointer to a <code>struct</code>.
|
even though under the covers it holds a pointer to a <code>struct</code>.
|
||||||
@ -838,8 +870,8 @@ to implement the three methods for that type, like this:
|
|||||||
<pre> <!-- progs/sortmain.go /type.day/ /Swap/ -->
|
<pre> <!-- progs/sortmain.go /type.day/ /Swap/ -->
|
||||||
30 type day struct {
|
30 type day struct {
|
||||||
31 num int;
|
31 num int;
|
||||||
32 short_name string;
|
32 shortName string;
|
||||||
33 long_name string;
|
33 longName string;
|
||||||
34 }
|
34 }
|
||||||
<p>
|
<p>
|
||||||
36 type dayArray struct {
|
36 type dayArray struct {
|
||||||
@ -972,7 +1004,7 @@ Schematically, given a value <code>v</code>, it does this:
|
|||||||
if ok {
|
if ok {
|
||||||
result = s.String()
|
result = s.String()
|
||||||
} else {
|
} else {
|
||||||
result = default_output(v)
|
result = defaultOutput(v)
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
@ -1002,7 +1034,7 @@ interface type defined in the <code>io</code> library:
|
|||||||
(This interface is another conventional name, this time for <code>Write</code>; there are also
|
(This interface is another conventional name, this time for <code>Write</code>; there are also
|
||||||
<code>io.Reader</code>, <code>io.ReadWriter</code>, and so on.)
|
<code>io.Reader</code>, <code>io.ReadWriter</code>, and so on.)
|
||||||
Thus you can call <code>Fprintf</code> on any type that implements a standard <code>Write()</code>
|
Thus you can call <code>Fprintf</code> on any type that implements a standard <code>Write()</code>
|
||||||
method, not just files but also network channels, buffers, rot13ers, whatever
|
method, not just files but also network channels, buffers, whatever
|
||||||
you want.
|
you want.
|
||||||
<p>
|
<p>
|
||||||
<h2>Prime numbers</h2>
|
<h2>Prime numbers</h2>
|
||||||
@ -1010,7 +1042,10 @@ you want.
|
|||||||
Now we come to processes and communication -- concurrent programming.
|
Now we come to processes and communication -- concurrent programming.
|
||||||
It's a big subject so to be brief we assume some familiarity with the topic.
|
It's a big subject so to be brief we assume some familiarity with the topic.
|
||||||
<p>
|
<p>
|
||||||
A classic program in the style is the prime sieve of Eratosthenes.
|
A classic program in the style is a prime sieve.
|
||||||
|
(The sieve of Eratosthenes is computationationally more efficient than
|
||||||
|
the algorithm presented here, but we are more interested in concurrency than
|
||||||
|
algorithmics at the moment.)
|
||||||
It works by taking a stream of all the natural numbers and introducing
|
It works by taking a stream of all the natural numbers and introducing
|
||||||
a sequence of filters, one for each prime, to winnow the multiples of
|
a sequence of filters, one for each prime, to winnow the multiples of
|
||||||
that prime. At each step we have a sequence of filters of the primes
|
that prime. At each step we have a sequence of filters of the primes
|
||||||
@ -1310,6 +1345,3 @@ at the end of main:
|
|||||||
<p>
|
<p>
|
||||||
There's a lot more to Go programming and concurrent programming in general but this
|
There's a lot more to Go programming and concurrent programming in general but this
|
||||||
quick tour should give you some of the basics.
|
quick tour should give you some of the basics.
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
@ -2,12 +2,15 @@
|
|||||||
Introduction
|
Introduction
|
||||||
----
|
----
|
||||||
|
|
||||||
This document is a tutorial introduction to the basics of the Go systems programming
|
This document is a tutorial introduction to the basics of the Go programming
|
||||||
language, intended for programmers familiar with C or C++. It is not a comprehensive
|
language, intended for programmers familiar with C or C++. It is not a comprehensive
|
||||||
guide to the language; at the moment the document closest to that is the
|
guide to the language; at the moment the document closest to that is the
|
||||||
<a href='/doc/go_spec.html'>language specification.</a>
|
<a href='/doc/go_spec.html'>language specification</a>.
|
||||||
|
After you've read this tutorial, you might want to look at
|
||||||
|
<a href='/doc/effective_go.html'>Effective Go</a>,
|
||||||
|
which digs deeper into how the language is used.
|
||||||
|
|
||||||
The presentation proceeds through a series of modest programs to illustrate
|
The presentation here proceeds through a series of modest programs to illustrate
|
||||||
key features of the language. All the programs work (at time of writing) and are
|
key features of the language. All the programs work (at time of writing) and are
|
||||||
checked into the repository in the directory <a href='/doc/progs'>"/doc/progs/"</a>.
|
checked into the repository in the directory <a href='/doc/progs'>"/doc/progs/"</a>.
|
||||||
|
|
||||||
@ -40,6 +43,32 @@ The comment convention is the same as in C++:
|
|||||||
|
|
||||||
Later we'll have much more to say about printing.
|
Later we'll have much more to say about printing.
|
||||||
|
|
||||||
|
Compiling
|
||||||
|
----
|
||||||
|
|
||||||
|
Go is a compiled language. At the moment there are two compilers.
|
||||||
|
"Gccgo" is a Go compiler that uses the GCC back end. There is also a
|
||||||
|
suite of compilers with different (and odd) names for each architecture:
|
||||||
|
"6g" for the 64-bit x86, "8g" for the 32-bit x86, and more. These
|
||||||
|
compilers run significantly faster but generate less efficient code
|
||||||
|
than "gccgo". At the time of writing (late 2009), they also have
|
||||||
|
a more robust run-time system although "gccgo" is catching up.
|
||||||
|
|
||||||
|
Here's how to compile and run our program. With "6g", say,
|
||||||
|
|
||||||
|
$ 6g helloworld.go # compile; object goes into helloworld.6
|
||||||
|
$ 6l helloworld.6 # link; output goes into 6.out
|
||||||
|
$ 6.out
|
||||||
|
Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
|
||||||
|
$
|
||||||
|
|
||||||
|
With "gccgo" it looks a little more traditional.
|
||||||
|
|
||||||
|
$ gccgo helloworld.go
|
||||||
|
$ a.out
|
||||||
|
Hello, world; or Καλημέρα κόσμε; or こんにちは 世界
|
||||||
|
$
|
||||||
|
|
||||||
Echo
|
Echo
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -48,10 +77,10 @@ Next up, here's a version of the Unix utility "echo(1)":
|
|||||||
--PROG progs/echo.go /package/ END
|
--PROG progs/echo.go /package/ END
|
||||||
|
|
||||||
This program is small but it's doing a number of new things. In the last example,
|
This program is small but it's doing a number of new things. In the last example,
|
||||||
we saw "func" introducing a function. The keywords "var", "const", and "type"
|
we saw "func" introduce a function. The keywords "var", "const", and "type"
|
||||||
(not used yet) also introduce declarations, as does "import".
|
(not used yet) also introduce declarations, as does "import".
|
||||||
Notice that we can group declarations of the same sort into
|
Notice that we can group declarations of the same sort into
|
||||||
parenthesized, semicolon-separated lists if we want, as on lines 4-10 and 14-17.
|
parenthesized, semicolon-separated lists if we want, as on lines 7-10 and 14-17.
|
||||||
But it's not necessary to do so; we could have said
|
But it's not necessary to do so; we could have said
|
||||||
|
|
||||||
const Space = " "
|
const Space = " "
|
||||||
@ -122,7 +151,7 @@ Later examples will show some other ways "for" can be written.
|
|||||||
|
|
||||||
The body of the loop builds up the string "s" by appending (using "+=")
|
The body of the loop builds up the string "s" by appending (using "+=")
|
||||||
the flags and separating spaces. After the loop, if the "-n" flag is not
|
the flags and separating spaces. After the loop, if the "-n" flag is not
|
||||||
set, it appends a newline, and then writes the result.
|
set, the program appends a newline. Finally, it writes the result.
|
||||||
|
|
||||||
Notice that "main.main" is a niladic function with no return type.
|
Notice that "main.main" is a niladic function with no return type.
|
||||||
It's defined that way. Falling off the end of "main.main" means
|
It's defined that way. Falling off the end of "main.main" means
|
||||||
@ -177,7 +206,7 @@ about pointers to arrays.
|
|||||||
The size of the array is part of its type; however, one can declare
|
The size of the array is part of its type; however, one can declare
|
||||||
a <i>slice</i> variable, to which one can assign a pointer to
|
a <i>slice</i> variable, to which one can assign a pointer to
|
||||||
any array
|
any array
|
||||||
with the same element type or - much more commonly - a <i>slice
|
with the same element type or—much more commonly—a <i>slice
|
||||||
expression</i> of the form "a[low : high]", representing
|
expression</i> of the form "a[low : high]", representing
|
||||||
the subarray indexed by "low" through "high-1".
|
the subarray indexed by "low" through "high-1".
|
||||||
Slices look a lot like arrays but have
|
Slices look a lot like arrays but have
|
||||||
@ -209,7 +238,7 @@ and invoke it like this:
|
|||||||
Note how the return type ("int") is defined for "sum()" by stating it
|
Note how the return type ("int") is defined for "sum()" by stating it
|
||||||
after the parameter list.
|
after the parameter list.
|
||||||
The expression "[3]int{1,2,3}" -- a type followed by a brace-bounded expression
|
The expression "[3]int{1,2,3}" -- a type followed by a brace-bounded expression
|
||||||
-- is a constructor for a value, in this case an array of 3 "ints". Putting an "&"
|
-- is a constructor for a value, in this case an array of 3 "ints". Putting an "&"
|
||||||
in front gives us the address of a unique instance of the value. We pass the
|
in front gives us the address of a unique instance of the value. We pass the
|
||||||
pointer to "sum()" by (automatically) promoting it to a slice.
|
pointer to "sum()" by (automatically) promoting it to a slice.
|
||||||
|
|
||||||
@ -219,13 +248,13 @@ elements for you, use "..." as the array size:
|
|||||||
s := sum(&[...]int{1,2,3});
|
s := sum(&[...]int{1,2,3});
|
||||||
|
|
||||||
In practice, though, unless you're meticulous about storage layout within a
|
In practice, though, unless you're meticulous about storage layout within a
|
||||||
data structure, a slice itself - using empty brackets and no "&" - is all you need:
|
data structure, a slice itself - using empty brackets and no "&" - is all you need:
|
||||||
|
|
||||||
s := sum([]int{1,2,3});
|
s := sum([]int{1,2,3});
|
||||||
|
|
||||||
There are also maps, which you can initialize like this:
|
There are also maps, which you can initialize like this:
|
||||||
|
|
||||||
m := map[string] int {"one":1 , "two":2}
|
m := map[string]int{"one":1 , "two":2}
|
||||||
|
|
||||||
The built-in function "len()", which returns number of elements,
|
The built-in function "len()", which returns number of elements,
|
||||||
makes its first appearance in "sum". It works on strings, arrays,
|
makes its first appearance in "sum". It works on strings, arrays,
|
||||||
@ -253,12 +282,12 @@ If you're holding a slice or a map and you modify its contents, other variables
|
|||||||
referencing the same underlying data will see the modification. For these three
|
referencing the same underlying data will see the modification. For these three
|
||||||
types you want to use the built-in function "make()":
|
types you want to use the built-in function "make()":
|
||||||
|
|
||||||
m := make(map[string] int);
|
m := make(map[string]int);
|
||||||
|
|
||||||
This statement initializes a new map ready to store entries.
|
This statement initializes a new map ready to store entries.
|
||||||
If you just declare the map, as in
|
If you just declare the map, as in
|
||||||
|
|
||||||
var m map[string] int;
|
var m map[string]int;
|
||||||
|
|
||||||
it creates a "nil" reference that cannot hold anything. To use the map,
|
it creates a "nil" reference that cannot hold anything. To use the map,
|
||||||
you must first initialize the reference using "make()" or by assignment to an
|
you must first initialize the reference using "make()" or by assignment to an
|
||||||
@ -273,7 +302,7 @@ An Interlude about Constants
|
|||||||
----
|
----
|
||||||
|
|
||||||
Although integers come in lots of sizes in Go, integer constants do not.
|
Although integers come in lots of sizes in Go, integer constants do not.
|
||||||
There are no constants like "0ll" or "0x0UL". Instead, integer
|
There are no constants like "0LL" or "0x0UL". Instead, integer
|
||||||
constants are evaluated as large-precision values that
|
constants are evaluated as large-precision values that
|
||||||
can overflow only when they are assigned to an integer variable with
|
can overflow only when they are assigned to an integer variable with
|
||||||
too little precision to represent the value.
|
too little precision to represent the value.
|
||||||
@ -321,8 +350,8 @@ that the file descriptor refers to.
|
|||||||
|
|
||||||
Because "File" starts with a capital letter, the type is available outside the package,
|
Because "File" starts with a capital letter, the type is available outside the package,
|
||||||
that is, by users of the package. In Go the rule about visibility of information is
|
that is, by users of the package. In Go the rule about visibility of information is
|
||||||
simple: if a name (of a top-level type, function, method, constant, variable, or of
|
simple: if a name (of a top-level type, function, method, constant or variable, or of
|
||||||
a structure field) is capitalized, users of the package may see it. Otherwise, the
|
a structure field or method) is capitalized, users of the package may see it. Otherwise, the
|
||||||
name and hence the thing being named is visible only inside the package in which
|
name and hence the thing being named is visible only inside the package in which
|
||||||
it is declared. This is more than a convention; the rule is enforced by the compiler.
|
it is declared. This is more than a convention; the rule is enforced by the compiler.
|
||||||
In Go, the term for publicly visible names is ''exported''.
|
In Go, the term for publicly visible names is ''exported''.
|
||||||
@ -390,7 +419,7 @@ the "struct" declaration itself. The "struct" declaration defines only data mem
|
|||||||
In fact, methods can be created for any type you name, such as an integer or
|
In fact, methods can be created for any type you name, such as an integer or
|
||||||
array, not just for "structs". We'll see an example with arrays later.
|
array, not just for "structs". We'll see an example with arrays later.
|
||||||
|
|
||||||
The "String" method is so called because of printing convention we'll
|
The "String" method is so called because of a printing convention we'll
|
||||||
describe later.
|
describe later.
|
||||||
|
|
||||||
The methods use the public variable "os.EINVAL" to return the ("os.Error"
|
The methods use the public variable "os.EINVAL" to return the ("os.Error"
|
||||||
@ -459,7 +488,7 @@ we have a second implementation of the "reader" interface.
|
|||||||
|
|
||||||
--PROG progs/cat_rot13.go /type.rotate13/ /end.of.rotate13/
|
--PROG progs/cat_rot13.go /type.rotate13/ /end.of.rotate13/
|
||||||
|
|
||||||
(The "rot13" function called on line 42 is trivial and not worth reproducing.)
|
(The "rot13" function called on line 42 is trivial and not worth reproducing here.)
|
||||||
|
|
||||||
To use the new feature, we define a flag:
|
To use the new feature, we define a flag:
|
||||||
|
|
||||||
@ -471,7 +500,7 @@ and use it from within a mostly unchanged "cat()" function:
|
|||||||
|
|
||||||
(We could also do the wrapping in "main" and leave "cat()" mostly alone, except
|
(We could also do the wrapping in "main" and leave "cat()" mostly alone, except
|
||||||
for changing the type of the argument; consider that an exercise.)
|
for changing the type of the argument; consider that an exercise.)
|
||||||
Lines 56 through 59 set it all up: If the "rot13" flag is true, wrap the "reader"
|
Lines 56 through 58 set it all up: If the "rot13" flag is true, wrap the "reader"
|
||||||
we received into a "rotate13" and proceed. Note that the interface variables
|
we received into a "rotate13" and proceed. Note that the interface variables
|
||||||
are values, not pointers: the argument is of type "reader", not "*reader",
|
are values, not pointers: the argument is of type "reader", not "*reader",
|
||||||
even though under the covers it holds a pointer to a "struct".
|
even though under the covers it holds a pointer to a "struct".
|
||||||
@ -630,7 +659,7 @@ Schematically, given a value "v", it does this:
|
|||||||
if ok {
|
if ok {
|
||||||
result = s.String()
|
result = s.String()
|
||||||
} else {
|
} else {
|
||||||
result = default_output(v)
|
result = defaultOutput(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
The code uses a ``type assertion'' ("v.(Stringer)") to test if the value stored in
|
The code uses a ``type assertion'' ("v.(Stringer)") to test if the value stored in
|
||||||
@ -657,7 +686,7 @@ interface type defined in the "io" library:
|
|||||||
(This interface is another conventional name, this time for "Write"; there are also
|
(This interface is another conventional name, this time for "Write"; there are also
|
||||||
"io.Reader", "io.ReadWriter", and so on.)
|
"io.Reader", "io.ReadWriter", and so on.)
|
||||||
Thus you can call "Fprintf" on any type that implements a standard "Write()"
|
Thus you can call "Fprintf" on any type that implements a standard "Write()"
|
||||||
method, not just files but also network channels, buffers, rot13ers, whatever
|
method, not just files but also network channels, buffers, whatever
|
||||||
you want.
|
you want.
|
||||||
|
|
||||||
Prime numbers
|
Prime numbers
|
||||||
@ -666,7 +695,10 @@ Prime numbers
|
|||||||
Now we come to processes and communication -- concurrent programming.
|
Now we come to processes and communication -- concurrent programming.
|
||||||
It's a big subject so to be brief we assume some familiarity with the topic.
|
It's a big subject so to be brief we assume some familiarity with the topic.
|
||||||
|
|
||||||
A classic program in the style is the prime sieve of Eratosthenes.
|
A classic program in the style is a prime sieve.
|
||||||
|
(The sieve of Eratosthenes is computationationally more efficient than
|
||||||
|
the algorithm presented here, but we are more interested in concurrency than
|
||||||
|
algorithmics at the moment.)
|
||||||
It works by taking a stream of all the natural numbers and introducing
|
It works by taking a stream of all the natural numbers and introducing
|
||||||
a sequence of filters, one for each prime, to winnow the multiples of
|
a sequence of filters, one for each prime, to winnow the multiples of
|
||||||
that prime. At each step we have a sequence of filters of the primes
|
that prime. At each step we have a sequence of filters of the primes
|
||||||
@ -825,6 +857,3 @@ at the end of main:
|
|||||||
|
|
||||||
There's a lot more to Go programming and concurrent programming in general but this
|
There's a lot more to Go programming and concurrent programming in general but this
|
||||||
quick tour should give you some of the basics.
|
quick tour should give you some of the basics.
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
@ -17,13 +17,13 @@ func cat(f *file.File) {
|
|||||||
for {
|
for {
|
||||||
switch nr, er := f.Read(&buf); true {
|
switch nr, er := f.Read(&buf); true {
|
||||||
case nr < 0:
|
case nr < 0:
|
||||||
fmt.Fprintf(os.Stderr, "error reading from %s: %s\n", f.String(), er.String());
|
fmt.Fprintf(os.Stderr, "cat: error reading from %s: %s\n", f.String(), er.String());
|
||||||
os.Exit(1);
|
os.Exit(1);
|
||||||
case nr == 0: // EOF
|
case nr == 0: // EOF
|
||||||
return;
|
return;
|
||||||
case nr > 0:
|
case nr > 0:
|
||||||
if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr {
|
if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr {
|
||||||
fmt.Fprintf(os.Stderr, "error writing from %s: %s\n", f.String(), ew.String());
|
fmt.Fprintf(os.Stderr, "cat: error writing from %s: %s\n", f.String(), ew.String());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ func main() {
|
|||||||
for i := 0; i < flag.NArg(); i++ {
|
for i := 0; i < flag.NArg(); i++ {
|
||||||
f, err := file.Open(flag.Arg(i), 0, 0);
|
f, err := file.Open(flag.Arg(i), 0, 0);
|
||||||
if f == nil {
|
if f == nil {
|
||||||
fmt.Fprintf(os.Stderr, "can't open %s: error %s\n", flag.Arg(i), err);
|
fmt.Fprintf(os.Stderr, "cat: can't open %s: error %s\n", flag.Arg(i), err);
|
||||||
os.Exit(1);
|
os.Exit(1);
|
||||||
}
|
}
|
||||||
cat(f);
|
cat(f);
|
||||||
|
@ -59,14 +59,14 @@ func cat(r reader) {
|
|||||||
for {
|
for {
|
||||||
switch nr, er := r.Read(&buf); {
|
switch nr, er := r.Read(&buf); {
|
||||||
case nr < 0:
|
case nr < 0:
|
||||||
fmt.Fprintf(os.Stderr, "error reading from %s: %s\n", r.String(), er.String());
|
fmt.Fprintf(os.Stderr, "cat: error reading from %s: %s\n", r.String(), er.String());
|
||||||
os.Exit(1);
|
os.Exit(1);
|
||||||
case nr == 0: // EOF
|
case nr == 0: // EOF
|
||||||
return;
|
return;
|
||||||
case nr > 0:
|
case nr > 0:
|
||||||
nw, ew := file.Stdout.Write(buf[0:nr]);
|
nw, ew := file.Stdout.Write(buf[0:nr]);
|
||||||
if nw != nr {
|
if nw != nr {
|
||||||
fmt.Fprintf(os.Stderr, "error writing from %s: %s\n", r.String(), ew.String());
|
fmt.Fprintf(os.Stderr, "cat: error writing from %s: %s\n", r.String(), ew.String());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ func main() {
|
|||||||
for i := 0; i < flag.NArg(); i++ {
|
for i := 0; i < flag.NArg(); i++ {
|
||||||
f, err := file.Open(flag.Arg(i), 0, 0);
|
f, err := file.Open(flag.Arg(i), 0, 0);
|
||||||
if f == nil {
|
if f == nil {
|
||||||
fmt.Fprintf(os.Stderr, "can't open %s: error %s\n", flag.Arg(i), err);
|
fmt.Fprintf(os.Stderr, "cat: can't open %s: error %s\n", flag.Arg(i), err);
|
||||||
os.Exit(1);
|
os.Exit(1);
|
||||||
}
|
}
|
||||||
cat(f);
|
cat(f);
|
||||||
|
@ -12,8 +12,8 @@ import (
|
|||||||
var n_flag = flag.Bool("n", false, "don't print final newline")
|
var n_flag = flag.Bool("n", false, "don't print final newline")
|
||||||
|
|
||||||
const (
|
const (
|
||||||
kSpace = " ";
|
Space = " ";
|
||||||
kNewline = "\n";
|
Newline = "\n";
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -21,12 +21,12 @@ func main() {
|
|||||||
var s string = "";
|
var s string = "";
|
||||||
for i := 0; i < flag.NArg(); i++ {
|
for i := 0; i < flag.NArg(); i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
s += kSpace
|
s += Space
|
||||||
}
|
}
|
||||||
s += flag.Arg(i)
|
s += flag.Arg(i)
|
||||||
}
|
}
|
||||||
if !*n_flag {
|
if !*n_flag {
|
||||||
s += kNewline
|
s += Newline
|
||||||
}
|
}
|
||||||
os.Stdout.WriteString(s);
|
os.Stdout.WriteString(s);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
fd int; // file descriptor number
|
fd int; // file descriptor number
|
||||||
name string; // file name at Open time
|
name string; // file name at Open time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,8 +29,8 @@ func strings() {
|
|||||||
|
|
||||||
type day struct {
|
type day struct {
|
||||||
num int;
|
num int;
|
||||||
short_name string;
|
shortName string;
|
||||||
long_name string;
|
longName string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type dayArray struct {
|
type dayArray struct {
|
||||||
@ -56,7 +56,7 @@ func days() {
|
|||||||
panic()
|
panic()
|
||||||
}
|
}
|
||||||
for _, d := range data {
|
for _, d := range data {
|
||||||
fmt.Printf("%s ", d.long_name)
|
fmt.Printf("%s ", d.longName)
|
||||||
}
|
}
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user