Update generics example to prepare for iteration

This commit is contained in:
Eli Bendersky
2024-08-21 06:43:09 -07:00
parent d51709a171
commit 6ed788f3f5
4 changed files with 51 additions and 47 deletions

View File

@@ -5,20 +5,22 @@ package main
import "fmt"
// As an example of a generic function, `MapKeys` takes
// a map of any type and returns a slice of its keys.
// This function has two type parameters - `K` and `V`;
// `K` has the `comparable` _constraint_, meaning that
// we can compare values of this type with the `==` and
// `!=` operators. This is required for map keys in Go.
// `V` has the `any` constraint, meaning that it's not
// restricted in any way (`any` is an alias for `interface{}`).
func MapKeys[K comparable, V any](m map[K]V) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
// As an example of a generic function, `SlicesIndex` takes
// a slice of any `comparable` type and an element of that
// type and returns the index of the first occurrence of
// v in s, or -1 if not present. The `comparable` constraint
// means that we can compare values of this type with the
// `==` and `!=` operators. For a more thorough explanation
// of this type signature, see [this blog post](https://go.dev/blog/deconstructing-type-parameters).
// Note that this function exists in the standard library
// as [slices.Index](https://pkg.go.dev/slices#Index).
func SlicesIndex[S ~[]E, E comparable](s S, v E) int {
for i := range s {
if v == s[i] {
return i
}
}
return r
return -1
}
// As an example of a generic type, `List` is a
@@ -45,7 +47,10 @@ func (lst *List[T]) Push(v T) {
}
}
func (lst *List[T]) GetAll() []T {
// AllElements returns all the List elements as a slice.
// In the next example we'll see a more idiomatic way
// of iterating over all elements of custom types.
func (lst *List[T]) AllElements() []T {
var elems []T
for e := lst.head; e != nil; e = e.next {
elems = append(elems, e.val)
@@ -54,21 +59,21 @@ func (lst *List[T]) GetAll() []T {
}
func main() {
var m = map[int]string{1: "2", 2: "4", 4: "8"}
var s = []string{"foo", "bar", "zoo"}
// When invoking generic functions, we can often rely
// on _type inference_. Note that we don't have to
// specify the types for `K` and `V` when
// calling `MapKeys` - the compiler infers them
// specify the types for `S` and `E` when
// calling `SlicesIndex` - the compiler infers them
// automatically.
fmt.Println("keys:", MapKeys(m))
fmt.Println("index of zoo:", SlicesIndex(s, "zoo"))
// ... though we could also specify them explicitly.
_ = MapKeys[int, string](m)
_ = SlicesIndex[[]string, string](s, "zoo")
lst := List[int]{}
lst.Push(10)
lst.Push(13)
lst.Push(23)
fmt.Println("list:", lst.GetAll())
fmt.Println("list:", lst.AllElements())
}

View File

@@ -1,2 +1,2 @@
91465956a90881ec8b4cca3968b9aa1f6d9f1447
uXlb-AyeYmQ
d6b4792fc509f0dcd84f15ed92097f52a73eb877
MNfKskDAZ6d

View File

@@ -1,7 +1,3 @@
$ go run generics.go
keys: [4 1 2]
index of zoo: 2
list: [10 13 23]
# Note: The order of iteration over map keys is not
# defined in Go, so different invocations may
# result in different orders.

43
public/generics generated
View File

@@ -45,7 +45,7 @@
</td>
<td class="code leading">
<a href="https://go.dev/play/p/uXlb-AyeYmQ"><img title="Run code" src="play.png" class="run" /></a><img title="Copy code" src="clipboard.png" class="copy" />
<a href="https://go.dev/play/p/MNfKskDAZ6d"><img title="Run code" src="play.png" class="run" /></a><img title="Copy code" src="clipboard.png" class="copy" />
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span></span></span></code></pre>
</td>
</tr>
@@ -62,24 +62,26 @@
<tr>
<td class="docs">
<p>As an example of a generic function, <code>MapKeys</code> takes
a map of any type and returns a slice of its keys.
This function has two type parameters - <code>K</code> and <code>V</code>;
<code>K</code> has the <code>comparable</code> <em>constraint</em>, meaning that
we can compare values of this type with the <code>==</code> and
<code>!=</code> operators. This is required for map keys in Go.
<code>V</code> has the <code>any</code> constraint, meaning that it&rsquo;s not
restricted in any way (<code>any</code> is an alias for <code>interface{}</code>).</p>
<p>As an example of a generic function, <code>SlicesIndex</code> takes
a slice of any <code>comparable</code> type and an element of that
type and returns the index of the first occurrence of
v in s, or -1 if not present. The <code>comparable</code> constraint
means that we can compare values of this type with the
<code>==</code> and <code>!=</code> operators. For a more thorough explanation
of this type signature, see <a href="https://go.dev/blog/deconstructing-type-parameters">this blog post</a>.
Note that this function exists in the standard library
as <a href="https://pkg.go.dev/slices#Index">slices.Index</a>.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="nx">MapKeys</span><span class="p">[</span><span class="nx">K</span> <span class="nx">comparable</span><span class="p">,</span> <span class="nx">V</span> <span class="nx">any</span><span class="p">](</span><span class="nx">m</span> <span class="kd">map</span><span class="p">[</span><span class="nx">K</span><span class="p">]</span><span class="nx">V</span><span class="p">)</span> <span class="p">[]</span><span class="nx">K</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="nx">K</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">m</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">k</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">m</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">r</span><span class="p">,</span> <span class="nx">k</span><span class="p">)</span>
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="nx">SlicesIndex</span><span class="p">[</span><span class="nx">S</span> <span class="err">~</span><span class="p">[]</span><span class="nx">E</span><span class="p">,</span> <span class="nx">E</span> <span class="nx">comparable</span><span class="p">](</span><span class="nx">s</span> <span class="nx">S</span><span class="p">,</span> <span class="nx">v</span> <span class="nx">E</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">s</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">v</span> <span class="o">==</span> <span class="nx">s</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">i</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">r</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre>
</td>
</tr>
@@ -155,7 +157,7 @@ parameters in place. The type is <code>List[T]</code>, not <code>List</code>.</p
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">m</span> <span class="p">=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">int</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span><span class="mi">1</span><span class="p">:</span> <span class="s">&#34;2&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="p">:</span> <span class="s">&#34;4&#34;</span><span class="p">,</span> <span class="mi">4</span><span class="p">:</span> <span class="s">&#34;8&#34;</span><span class="p">}</span></span></span></code></pre>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">s</span> <span class="p">=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;foo&#34;</span><span class="p">,</span> <span class="s">&#34;bar&#34;</span><span class="p">,</span> <span class="s">&#34;zoo&#34;</span><span class="p">}</span></span></span></code></pre>
</td>
</tr>
@@ -163,14 +165,14 @@ parameters in place. The type is <code>List[T]</code>, not <code>List</code>.</p
<td class="docs">
<p>When invoking generic functions, we can often rely
on <em>type inference</em>. Note that we don&rsquo;t have to
specify the types for <code>K</code> and <code>V</code> when
calling <code>MapKeys</code> - the compiler infers them
specify the types for <code>S</code> and <code>E</code> when
calling <code>SlicesIndex</code> - the compiler infers them
automatically.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;keys:&#34;</span><span class="p">,</span> <span class="nf">MapKeys</span><span class="p">(</span><span class="nx">m</span><span class="p">))</span></span></span></code></pre>
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;index of foo:&#34;</span><span class="p">,</span> <span class="nf">SlicesIndex</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span> <span class="s">&#34;foo&#34;</span><span class="p">))</span></span></span></code></pre>
</td>
</tr>
@@ -181,7 +183,8 @@ automatically.</p>
</td>
<td class="code leading">
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">MapKeys</span><span class="p">[</span><span class="kt">int</span><span class="p">,</span> <span class="kt">string</span><span class="p">](</span><span class="nx">m</span><span class="p">)</span></span></span></code></pre>
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="c1">//_ = MapKeys[int, string](m)
</span></span></span></code></pre>
</td>
</tr>
@@ -244,7 +247,7 @@ result in different orders.</p>
</div>
<script>
var codeLines = [];
codeLines.push('');codeLines.push('package main\u000A');codeLines.push('import \"fmt\"\u000A');codeLines.push('func MapKeys[K comparable, V any](m map[K]V) []K {\u000A r :\u003D make([]K, 0, len(m))\u000A for k :\u003D range m {\u000A r \u003D append(r, k)\u000A }\u000A return r\u000A}\u000A');codeLines.push('type List[T any] struct {\u000A head, tail *element[T]\u000A}\u000A');codeLines.push('type element[T any] struct {\u000A next *element[T]\u000A val T\u000A}\u000A');codeLines.push('func (lst *List[T]) Push(v T) {\u000A if lst.tail \u003D\u003D nil {\u000A lst.head \u003D \u0026element[T]{val: v}\u000A lst.tail \u003D lst.head\u000A } else {\u000A lst.tail.next \u003D \u0026element[T]{val: v}\u000A lst.tail \u003D lst.tail.next\u000A }\u000A}\u000A');codeLines.push('func (lst *List[T]) GetAll() []T {\u000A var elems []T\u000A for e :\u003D lst.head; e !\u003D nil; e \u003D e.next {\u000A elems \u003D append(elems, e.val)\u000A }\u000A return elems\u000A}\u000A');codeLines.push('func main() {\u000A var m \u003D map[int]string{1: \"2\", 2: \"4\", 4: \"8\"}\u000A');codeLines.push(' fmt.Println(\"keys:\", MapKeys(m))\u000A');codeLines.push(' _ \u003D MapKeys[int, string](m)\u000A');codeLines.push(' lst :\u003D List[int]{}\u000A lst.Push(10)\u000A lst.Push(13)\u000A lst.Push(23)\u000A fmt.Println(\"list:\", lst.GetAll())\u000A}\u000A');codeLines.push('');codeLines.push('');
codeLines.push('');codeLines.push('package main\u000A');codeLines.push('import \"fmt\"\u000A');codeLines.push('func SlicesIndex[S ~[]E, E comparable](s S, v E) int {\u000A for i :\u003D range s {\u000A if v \u003D\u003D s[i] {\u000A return i\u000A }\u000A }\u000A return -1\u000A}\u000A');codeLines.push('type List[T any] struct {\u000A head, tail *element[T]\u000A}\u000A');codeLines.push('type element[T any] struct {\u000A next *element[T]\u000A val T\u000A}\u000A');codeLines.push('func (lst *List[T]) Push(v T) {\u000A if lst.tail \u003D\u003D nil {\u000A lst.head \u003D \u0026element[T]{val: v}\u000A lst.tail \u003D lst.head\u000A } else {\u000A lst.tail.next \u003D \u0026element[T]{val: v}\u000A lst.tail \u003D lst.tail.next\u000A }\u000A}\u000A');codeLines.push('func (lst *List[T]) GetAll() []T {\u000A var elems []T\u000A for e :\u003D lst.head; e !\u003D nil; e \u003D e.next {\u000A elems \u003D append(elems, e.val)\u000A }\u000A return elems\u000A}\u000A');codeLines.push('func main() {\u000A var s \u003D []string{\"foo\", \"bar\", \"zoo\"}\u000A');codeLines.push(' fmt.Println(\"index of foo:\", SlicesIndex(s, \"foo\"))\u000A');codeLines.push(' //_ \u003D MapKeys[int, string](m)\u000A');codeLines.push(' lst :\u003D List[int]{}\u000A lst.Push(10)\u000A lst.Push(13)\u000A lst.Push(23)\u000A fmt.Println(\"list:\", lst.GetAll())\u000A}\u000A');codeLines.push('');codeLines.push('');
</script>
<script src="site.js" async></script>
</body>