296 lines
20 KiB
Plaintext
Generated
296 lines
20 KiB
Plaintext
Generated
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Go by Example: Range over Iterators</title>
|
|
<link rel=stylesheet href="site.css">
|
|
</head>
|
|
<script>
|
|
window.onkeydown = (e) => {
|
|
if (e.ctrlKey || e.altKey || e.shiftKey) {
|
|
return;
|
|
}
|
|
|
|
if (e.key == "ArrowLeft") {
|
|
window.location.href = 'generics';
|
|
}
|
|
|
|
|
|
if (e.key == "ArrowRight") {
|
|
window.location.href = 'errors';
|
|
}
|
|
|
|
}
|
|
</script>
|
|
<body>
|
|
<div class="example" id="range-over-iterators">
|
|
<h2><a href="./">Go by Example</a>: Range over Iterators</h2>
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
<p>Starting with version 1.23, Go has added support for
|
|
<a href="https://go.dev/blog/range-functions">iterators</a>,
|
|
which lets us range over pretty much anything!</p>
|
|
|
|
</td>
|
|
<td class="code empty leading">
|
|
|
|
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
|
|
</td>
|
|
<td class="code leading">
|
|
<a href="https://go.dev/play/p/BayyagaCK83"><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>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
|
|
</td>
|
|
<td class="code leading">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="s">"iter"</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="s">"slices"</span>
|
|
</span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
<p>Let’s look at the <code>List</code> type from the
|
|
<a href="generics">previous example</a> again. In that example
|
|
we had an <code>AllElements</code> method that returned a slice
|
|
of all elements in the list. With Go iterators, we
|
|
can do it better - as shown below.</p>
|
|
|
|
</td>
|
|
<td class="code leading">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">List</span><span class="p">[</span><span class="nx">T</span> <span class="nx">any</span><span class="p">]</span> <span class="kd">struct</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="nx">head</span><span class="p">,</span> <span class="nx">tail</span> <span class="o">*</span><span class="nx">element</span><span class="p">[</span><span class="nx">T</span><span class="p">]</span>
|
|
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
|
|
</td>
|
|
<td class="code leading">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">element</span><span class="p">[</span><span class="nx">T</span> <span class="nx">any</span><span class="p">]</span> <span class="kd">struct</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="nx">next</span> <span class="o">*</span><span class="nx">element</span><span class="p">[</span><span class="nx">T</span><span class="p">]</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="nx">val</span> <span class="nx">T</span>
|
|
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
|
|
</td>
|
|
<td class="code leading">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">lst</span> <span class="o">*</span><span class="nx">List</span><span class="p">[</span><span class="nx">T</span><span class="p">])</span> <span class="nf">Push</span><span class="p">(</span><span class="nx">v</span> <span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">lst</span><span class="p">.</span><span class="nx">tail</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="nx">lst</span><span class="p">.</span><span class="nx">head</span> <span class="p">=</span> <span class="o">&</span><span class="nx">element</span><span class="p">[</span><span class="nx">T</span><span class="p">]{</span><span class="nx">val</span><span class="p">:</span> <span class="nx">v</span><span class="p">}</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="nx">lst</span><span class="p">.</span><span class="nx">tail</span> <span class="p">=</span> <span class="nx">lst</span><span class="p">.</span><span class="nx">head</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="nx">lst</span><span class="p">.</span><span class="nx">tail</span><span class="p">.</span><span class="nx">next</span> <span class="p">=</span> <span class="o">&</span><span class="nx">element</span><span class="p">[</span><span class="nx">T</span><span class="p">]{</span><span class="nx">val</span><span class="p">:</span> <span class="nx">v</span><span class="p">}</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="nx">lst</span><span class="p">.</span><span class="nx">tail</span> <span class="p">=</span> <span class="nx">lst</span><span class="p">.</span><span class="nx">tail</span><span class="p">.</span><span class="nx">next</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></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
<p>All returns an <em>iterator</em>, which in Go is a function
|
|
with a <a href="https://pkg.go.dev/iter#Seq">special signature</a>.</p>
|
|
|
|
</td>
|
|
<td class="code leading">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">lst</span> <span class="o">*</span><span class="nx">List</span><span class="p">[</span><span class="nx">T</span><span class="p">])</span> <span class="nf">All</span><span class="p">()</span> <span class="nx">iter</span><span class="p">.</span><span class="nx">Seq</span><span class="p">[</span><span class="nx">T</span><span class="p">]</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">yield</span> <span class="kd">func</span><span class="p">(</span><span class="nx">T</span><span class="p">)</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
<p>The iterator function takes another function as
|
|
a parameter, called <code>yield</code> by convention (but
|
|
the name can be arbitrary). It will call <code>yield</code> for
|
|
every element we want to iterate over, and note <code>yield</code>’s
|
|
return value for a potential early termination.</p>
|
|
|
|
</td>
|
|
<td class="code leading">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">e</span> <span class="o">:=</span> <span class="nx">lst</span><span class="p">.</span><span class="nx">head</span><span class="p">;</span> <span class="nx">e</span> <span class="o">!=</span> <span class="kc">nil</span><span class="p">;</span> <span class="nx">e</span> <span class="p">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">next</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nf">yield</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="k">return</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="p">}</span>
|
|
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
<p>Iteration doesn’t require an underlying data structure,
|
|
and doesn’t even have to be finite! Here’s a function
|
|
returning an iterator over Fibonacci numbers: it keeps
|
|
running as long as <code>yield</code> keeps returning <code>true</code>.</p>
|
|
|
|
</td>
|
|
<td class="code leading">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">genFib</span><span class="p">()</span> <span class="nx">iter</span><span class="p">.</span><span class="nx">Seq</span><span class="p">[</span><span class="kt">int</span><span class="p">]</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">yield</span> <span class="kd">func</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="o">:=</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
|
|
</td>
|
|
<td class="code leading">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nf">yield</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="p">=</span> <span class="nx">b</span><span class="p">,</span> <span class="nx">a</span><span class="o">+</span><span class="nx">b</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="p">}</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
|
|
</td>
|
|
<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="nx">lst</span> <span class="o">:=</span> <span class="nx">List</span><span class="p">[</span><span class="kt">int</span><span class="p">]{}</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="nx">lst</span><span class="p">.</span><span class="nf">Push</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="nx">lst</span><span class="p">.</span><span class="nf">Push</span><span class="p">(</span><span class="mi">13</span><span class="p">)</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="nx">lst</span><span class="p">.</span><span class="nf">Push</span><span class="p">(</span><span class="mi">23</span><span class="p">)</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
<p>Since <code>List.All</code> returns an iterator, we can use it
|
|
in a regular <code>range</code> loop.</p>
|
|
|
|
</td>
|
|
<td class="code leading">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">e</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">lst</span><span class="p">.</span><span class="nf">All</span><span class="p">()</span> <span class="p">{</span>
|
|
</span></span><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="nx">e</span><span class="p">)</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
<p>Packages like <a href="https://pkg.go.dev/slices">slices</a> have
|
|
a number of useful functions to work with iterators.
|
|
For example, <code>Collect</code> takes any iterator and collects
|
|
all its values into a slice.</p>
|
|
|
|
</td>
|
|
<td class="code leading">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="nx">all</span> <span class="o">:=</span> <span class="nx">slices</span><span class="p">.</span><span class="nf">Collect</span><span class="p">(</span><span class="nx">lst</span><span class="p">.</span><span class="nf">All</span><span class="p">())</span>
|
|
</span></span><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">"all:"</span><span class="p">,</span> <span class="nx">all</span><span class="p">)</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
|
|
</td>
|
|
<td class="code leading">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">n</span> <span class="o">:=</span> <span class="k">range</span> <span class="nf">genFib</span><span class="p">()</span> <span class="p">{</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
<p>Once the loop hits <code>break</code> or an early return, the <code>yield</code> function
|
|
passed to the iterator will return <code>false</code>, stopping the iterator.</p>
|
|
|
|
</td>
|
|
<td class="code">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">n</span> <span class="o">>=</span> <span class="mi">10</span> <span class="p">{</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="k">break</span>
|
|
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
|
|
</span></span><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="nx">n</span><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="p">}</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
<td class="docs">
|
|
|
|
</td>
|
|
<td class="code">
|
|
|
|
<pre class="chroma"><code><span class="line"><span class="cl"><span class="go">10
|
|
</span></span></span><span class="line"><span class="cl"><span class="go">13
|
|
</span></span></span><span class="line"><span class="cl"><span class="go">23
|
|
</span></span></span><span class="line"><span class="cl"><span class="go">all: [10 13 23]
|
|
</span></span></span><span class="line"><span class="cl"><span class="go">1
|
|
</span></span></span><span class="line"><span class="cl"><span class="go">1
|
|
</span></span></span><span class="line"><span class="cl"><span class="go">2
|
|
</span></span></span><span class="line"><span class="cl"><span class="go">3
|
|
</span></span></span><span class="line"><span class="cl"><span class="go">5
|
|
</span></span></span><span class="line"><span class="cl"><span class="go">8</span></span></span></code></pre>
|
|
</td>
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
|
|
<p class="next">
|
|
Next example: <a href="errors">Errors</a>.
|
|
</p>
|
|
|
|
|
|
<p class="footer">
|
|
by <a href="https://markmcgranaghan.com">Mark McGranaghan</a> and <a href="https://eli.thegreenplace.net">Eli Bendersky</a> | <a href="https://github.com/mmcgrana/gobyexample">source</a> | <a href="https://github.com/mmcgrana/gobyexample#license">license</a>
|
|
</p>
|
|
|
|
</div>
|
|
<script>
|
|
var codeLines = [];
|
|
codeLines.push('');codeLines.push('package main\u000A');codeLines.push('import (\u000A \"fmt\"\u000A \"iter\"\u000A \"slices\"\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]) All() iter.Seq[T] {\u000A return func(yield func(T) bool) {\u000A');codeLines.push(' for e :\u003D lst.head; e !\u003D nil; e \u003D e.next {\u000A if !yield(e.val) {\u000A return\u000A }\u000A }\u000A }\u000A}\u000A');codeLines.push('func genFib() iter.Seq[int] {\u000A return func(yield func(int) bool) {\u000A a, b :\u003D 1, 1\u000A');codeLines.push(' for {\u000A if !yield(a) {\u000A return\u000A }\u000A a, b \u003D b, a+b\u000A }\u000A }\u000A}\u000A');codeLines.push('func main() {\u000A lst :\u003D List[int]{}\u000A lst.Push(10)\u000A lst.Push(13)\u000A lst.Push(23)\u000A');codeLines.push(' for e :\u003D range lst.All() {\u000A fmt.Println(e)\u000A }\u000A');codeLines.push(' all :\u003D slices.Collect(lst.All())\u000A fmt.Println(\"all:\", all)\u000A');codeLines.push(' for n :\u003D range genFib() {\u000A');codeLines.push(' if n \u003E\u003D 10 {\u000A break\u000A }\u000A fmt.Println(n)\u000A }\u000A}\u000A');codeLines.push('');
|
|
</script>
|
|
<script src="site.js" async></script>
|
|
</body>
|
|
</html>
|