Skip to content

Instantly share code, notes, and snippets.

@zoffixznet
Created December 8, 2016 16:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zoffixznet/64de2db8242754e790b35f366604121e to your computer and use it in GitHub Desktop.
Save zoffixznet/64de2db8242754e790b35f366604121e to your computer and use it in GitHub Desktop.
<p>There's a relatively common pattern I see with people writing code that counts... say, DNA bases in a string:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">%</span><span style="color: #440; font-weight: bold">counts</span>;
<span style="color: #440; font-weight: bold">%</span><span style="color: #440; font-weight: bold">counts</span>{<span style="color: blue;">$_</span>}<span style="color: blue;">++</span> <span style="color: blue;">for</span> <span style="color: #994;"><span style="font-weight: bold;">'</span>AGTCAGTCAGTCTTTCCCAAAAT<span style="font-weight: bold;">'</span></span><span style="color: blue;">.</span><span style="color: #449;">comb</span>;
<span style="color: #449;">say</span> <span style="color: #440; font-weight: bold">%</span><span style="color: #440; font-weight: bold">counts</span><span style="color: blue;">&lt;</span><span style="color: #994;">A T G C</span><span style="color: blue;">&gt;</span>; <span style="color: #999;"># (7 7 3 6)</span></pre></div>
<p>Make a <a href="https://docs.perl6.org/type/Hash"><code>Hash</code></a>. For each thing you want to count, <code>++</code> that key in that <code>Hash</code>. So what's the problem?</p>
<p>Perl 6 actually has specialized types that are more appropriate for this operation; for example, the <a href="https://docs.perl6.org/type/Bag"><code>Bag</code></a>:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: #994;"><span style="font-weight: bold;">'</span>AGTCAGTCAGTCTTTCCCAAAAT<span style="font-weight: bold;">'</span></span><span style="color: blue;">.</span><span style="color: #449;">comb</span><span style="color: blue;">.</span><span style="color: #449;">Bag</span><span style="color: blue;">&lt;</span><span style="color: #994;">A T G C</span><span style="color: blue;">&gt;.</span><span style="color: #449;">say</span>; <span style="color: #999;"># (7 7 3 6)</span></pre></div>
<p>Let's talk about these types and all the fancy operators that come with them!</p>
<h2><a id="user-content-a-note-on-unicode" class="anchor" href="#a-note-on-unicode" aria-hidden="true"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>A Note on Unicode</h2>
<p>I'll be using fancy-pants Unicode versions of operators and symbols in this post, because they look purty. However, all of them have what we call <a href="https://docs.perl6.org/language/unicode_texas.html#Other_acceptable_single_codepoints">"Texas" equivalents you can use instead</a>.</p>
<h2><a id="user-content-ready-set-go" class="anchor" href="#ready-set-go" aria-hidden="true"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Ready. Set. Go.</h2>
<p>The simplest of these types is a <a href="https://docs.perl6.org/type/Set"><code>Set</code></a>. It will keep exactly one of each item, so if you have multiple objects that are the same, those will be discarded:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: #449;">say</span> set <span style="color: #449;">1</span>, <span style="color: #449;">2</span>, <span style="color: #449;">2</span>, <span style="color: #994;"><span style="font-weight: bold;">"</span>foo<span style="font-weight: bold;">"</span></span>, <span style="color: #994;"><span style="font-weight: bold;">"</span>a<span style="font-weight: bold;">"</span></span>, <span style="color: #994;"><span style="font-weight: bold;">"</span>a<span style="font-weight: bold;">"</span></span>, <span style="color: #994;"><span style="font-weight: bold;">"</span>a<span style="font-weight: bold;">"</span></span>, <span style="color: #994;"><span style="font-weight: bold;">"</span>a<span style="font-weight: bold;">"</span></span>, <span style="color: #994;"><span style="font-weight: bold;">"</span>b<span style="font-weight: bold;">"</span></span>;
<span style="color: #999;"># OUTPUT: set(a, foo, b, 1, 2)</span></pre></div>
<p>As you can see, the result has only one <code>a</code> and only one <code>2</code>. We can use set membership operator to check if an item is in a set:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">mah-peeps</span> <span style="color: blue;">=</span> set <span style="color: blue;">&lt;</span><span style="color: #994;">babydrop iBakeCake Zoffix viki</span><span style="color: blue;">&gt;</span>;
<span style="color: #449;">say</span> <span style="color: #994;"><span style="font-weight: bold;">'</span>Weeee \o/<span style="font-weight: bold;">'</span></span> <span style="color: blue;">if</span> <span style="color: #994;"><span style="font-weight: bold;">'</span>Zoffix<span style="font-weight: bold;">'</span></span> <span style="color: blue;">∈</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">mah-peeps</span>;
<span style="color: #999;"># OUTPUT: Weeee \o/</span></pre></div>
<p>The set operators are coercive, so we don't need to explicitly create a set; they'll do it for us:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: #449;">say</span> <span style="color: #994;"><span style="font-weight: bold;">'</span>Weeee \o/<span style="font-weight: bold;">'</span></span> <span style="color: blue;">if</span> <span style="color: #994;"><span style="font-weight: bold;">'</span>Zoffix<span style="font-weight: bold;">'</span></span> <span style="color: blue;">∈</span> <span style="color: blue;">&lt;</span><span style="color: #994;">babydrop iBakeCake Zoffix viki</span><span style="color: blue;">&gt;</span>;
<span style="color: #999;"># OUTPUT: Weeee \o/</span></pre></div>
<p>But pay attention when using <a href="https://docs.perl6.org/language/glossary#index-entry-Allomorph">allomorphs</a>:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: #449;">say</span> <span style="color: #994;"><span style="font-weight: bold;">'</span>Weeee \o/<span style="font-weight: bold;">'</span></span> <span style="color: blue;">if</span> <span style="color: #449;">42</span> <span style="color: blue;">∈</span> <span style="color: blue;">&lt;</span><span style="color: #994;">1 42 72</span><span style="color: blue;">&gt;</span>;
<span style="color: #999;"># No output</span>
<span style="color: #449;">say</span> <span style="color: #994;"><span style="font-weight: bold;">'</span>Weeee \o/<span style="font-weight: bold;">'</span></span> <span style="color: blue;">if</span> <span style="color: #449;">42</span> <span style="color: blue;">∈</span> <span style="color: blue;">+</span>«<span style="color: blue;">&lt;</span><span style="color: #994;">1 42 72</span><span style="color: blue;">&gt;</span>; <span style="color: #999;"># coerce allomorphs to Numeric</span>
<span style="color: #999;"># OUTPUT: Weeee \o/</span></pre></div>
<p>The <a href="https://docs.perl6.org/language/quoting#Word_quoting:_%3C_%3E">angle brackets create allomorphs</a> for numerics, so in the first case above, our set contains a bunch of <a href="https://docs.perl6.org/type/IntStr"><code>IntStr</code></a> objects, while the left hand side of the operator has a regular <a href="https://docs.perl6.org/type/IntStr"><code>Int</code></a>, and so the comparison fails. In the second case, we coerce allomorphs to their numeric component with a hyper operator and the test succeeds.</p>
<p>While testing membership is super exciting, we can do more with our sets! How about some intersections?</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">admins</span> <span style="color: blue;">=</span> set <span style="color: blue;">&lt;</span><span style="color: #994;">Zoffix mst [Coke] lizmat</span><span style="color: blue;">&gt;</span>;
<span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">live-in-North-America</span> <span style="color: blue;">=</span> set <span style="color: blue;">&lt;</span><span style="color: #994;">Zoffix [Coke] TimToady huggable</span><span style="color: blue;">&gt;</span>;
<span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">North-American-admins</span> <span style="color: blue;">=</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">admins</span> <span style="color: blue;">∩</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">live-in-North-America</span>;
<span style="color: #449;">say</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">North-American-admins</span>;
<span style="color: #999;"># OUTPUT: set(Zoffix, [Coke])</span></pre></div>
<p>We intersected two sets with the ∩ intersection operator and received a set that contains only the elements present in <em>both</em> original sets. You can chain these operations too, so membership will be checked in all of the provided sets in the chain:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: #449;">say</span> <span style="color: blue;">&lt;</span><span style="color: #994;">Zoffix lizmat</span><span style="color: blue;">&gt;</span> <span style="color: blue;">∩</span> <span style="color: blue;">&lt;</span><span style="color: #994;">huggable Zoffix</span><span style="color: blue;">&gt;</span> <span style="color: blue;">∩</span> <span style="color: blue;">&lt;</span><span style="color: #994;">TimToady huggable Zoffix</span><span style="color: blue;">&gt;</span>;
<span style="color: #999;"># OUTPUT: set(Zoffix)</span></pre></div>
<p>Another handy operator is the set difference operator, whose Unicode look I find somewhat annoying: <code>∖</code> No, it's not a backslash (<code>\</code>), but a <code>U+2216 SET MINUS</code> character (luckily, you can use the much more obvious <code>(-)</code> Texas version).</p>
<p>The usefulness of the operator compensates its shoddy looks:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">@</span><span style="color: #440; font-weight: bold">spammers</span> <span style="color: blue;">=</span> <span style="color: blue;">&lt;</span><span style="color: #994;">spammety@sam.com spam@in-a-can.com yum@spam.com</span><span style="color: blue;">&gt;</span>;
<span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">@</span><span style="color: #440; font-weight: bold">senders</span> <span style="color: blue;">=</span> <span style="color: blue;">&lt;</span>
perl6-compiler<span style="color: #440; font-weight: bold">@</span><span style="color: #440; font-weight: bold">perl6</span><span style="color: blue;">.</span>org spammety<span style="color: #440; font-weight: bold">@</span><span style="color: #440; font-weight: bold">sam</span><span style="color: blue;">.</span>com
example<span style="color: #440; font-weight: bold">@</span><span style="color: #440; font-weight: bold">example</span><span style="color: blue;">.</span>com good<span style="color: #440; font-weight: bold">@</span><span style="color: #440; font-weight: bold">guy</span><span style="color: blue;">.</span>com
<span style="color: blue;">&gt;</span>;
<span style="color: blue;">for</span> <span style="color: #449;">keys</span> <span style="color: #440; font-weight: bold">@</span><span style="color: #440; font-weight: bold">senders</span> <span style="color: blue;">∖</span> <span style="color: #440; font-weight: bold">@</span><span style="color: #440; font-weight: bold">spammers</span> <span style="color: blue;">-&gt;</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">non-spammer</span> {
<span style="color: #449;">say</span> <span style="color: #994;"><span style="font-weight: bold;">"</span>Message from <span style="color: #440; font-weight: bold">$non-spammer</span><span style="font-weight: bold;">"</span></span>;
}
<span style="color: #999;"># OUTPUT:</span>
<span style="color: #999;"># Message from perl6-compiler@perl6.org</span>
<span style="color: #999;"># Message from good@guy.com</span>
<span style="color: #999;"># Message from example@example.com</span></pre></div>
<p>We have two arrays: one contains a list of spammers' addresses and another contains a list of senders. How to get a list of senders, without any spammers in it? Just use the <code>∖</code> (fine, fine, the <code>(-)</code>) operator!</p>
<p>We then use the <code>for</code> loop to iterate over the results, and as you can see from the output, all spammers were omitted... But why is <code>keys</code> there?</p>
<p>The reason is <code>Setty</code> and <code>Mixy</code> types are a lot like hashes, in a sense that they have keys and values for those keys. <code>Set</code> types always have <code>True</code> as values, and since we don't care about iterating over <a href="https://docs.perl6.org/type/Pair"><code>Pair</code></a> objects in our loop, we use the <code>keys</code> to get just the keys of the set: the email addresses.</p>
<p>However, hash-like semantics aren't useless on <code>Set</code>s. For example, we can take a slice, and with <code>:k</code> adverb return just the elements that the set contains:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">meows</span> <span style="color: blue;">=</span> set <span style="color: blue;">&lt;</span>
Abyssinian Aegean Manx Siamese Siberian Snowshoe
Sokoke Sphynx Suphalak Thai
<span style="color: blue;">&gt;</span>;
<span style="color: #449;">say</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">meows</span><span style="color: blue;">&lt;</span><span style="color: #994;">Sphynx Raas Ragamuffin Thai</span><span style="color: blue;">&gt;:</span>k;
<span style="color: #999;"># OUTPUT: (Sphynx Thai)</span></pre></div>
<p>But what happens if we try to delete an item from a set?</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: #449;">say</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">meows</span><span style="color: blue;">&lt;</span><span style="color: #994;">Siamese</span><span style="color: blue;">&gt;:</span>delete;
<span style="color: #999;"># Cannot call 'DELETE-KEY' on an immutable 'Set'</span>
<span style="color: #999;"># in block &lt;unit&gt; at z.p6 line 6</span></pre></div>
<p>We can't! The <code>Set</code> type is immutable. However, just like <a href="https://docs.perl6.org/type/Map"><code>Map</code> type</a> has a mutable version <a href="https://docs.perl6.org/type/Hash"><code>Hash</code></a>, so does the <a href="https://docs.perl6.org/type/Set"><code>Set</code> type</a> has a mutable version: the <a href="https://docs.perl6.org/type/SetHash"><code>SetHash</code></a>. There isn't a cutesy helper sub to create one, so we'll use the constructor instead:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">s</span> <span style="color: blue;">=</span> <span style="color: #449;">SetHash</span><span style="color: blue;">.</span><span style="color: #449;">new</span><span style="color: blue;">:</span> <span style="color: blue;">&lt;</span><span style="color: #994;">a a a b c d</span><span style="color: blue;">&gt;</span>;
<span style="color: #449;">say</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">s</span>;
<span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">s</span><span style="color: blue;">&lt;</span><span style="color: #994;">a d</span><span style="color: blue;">&gt;:</span>delete;
<span style="color: #449;">say</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">s</span>;
<span style="color: #999;"># SetHash.new(a, c, b, d)</span>
<span style="color: #999;"># SetHash.new(c, b)</span></pre></div>
<p>Voilà! We successfully deleted a slice. So this stuff is nice and cool, but what other goodies does Santa have in his... bag?</p>
<h2><a id="user-content-gag-em-n-bag-em" class="anchor" href="#gag-em-n-bag-em" aria-hidden="true"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Gag 'em 'n' Bag 'em</h2>
<p>Related to Sets is another type: a <a href="https://docs.perl6.org/type/Bag"><code>Bag</code></a>, and yes, it's also immutable, with the corresponding mutable type being <a href="https://docs.perl6.org/type/BagHash"><code>BagHash</code></a>. We already saw at the start of this article we can use a <code>Bag</code> to count stuff, and just like a <code>Set</code>, a <code>Bag</code> is hash-like, which is why we could view a slice of the four DNA amino acids:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: #994;"><span style="font-weight: bold;">'</span>AGTCAGTCAGTCTTTCCCAAAAT<span style="font-weight: bold;">'</span></span><span style="color: blue;">.</span><span style="color: #449;">comb</span><span style="color: blue;">.</span><span style="color: #449;">Bag</span><span style="color: blue;">&lt;</span><span style="color: #994;">A T G C</span><span style="color: blue;">&gt;.</span><span style="color: #449;">say</span>; <span style="color: #999;"># (7 7 3 6)</span></pre></div>
<p>While a <code>Set</code> has all values set to <code>True</code>, a <code>Bag</code>'s values are integer weights. If you put two things that are the same into a <code>Bag</code> there'll be just one key for them, but the value will be <code>2</code>:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">recipe</span> <span style="color: blue;">=</span> bag <span style="color: #994;"><span style="font-weight: bold;">'</span>egg<span style="font-weight: bold;">'</span></span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>egg<span style="font-weight: bold;">'</span></span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>cup of milk<span style="font-weight: bold;">'</span></span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>cup of flour<span style="font-weight: bold;">'</span></span>;
<span style="color: #449;">say</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">recipe</span>;
<span style="color: #999;"># OUTPUT: bag(cup of flour, egg(2), cup of milk)</span></pre></div>
<p>And of course, we can use our handy operators to combine bags! Here, we'll be using <code>⊎</code>, <code>U+228E MULTISET UNION</code>, operator, which looks a lot clearer in its Texas version: <code>(+)</code></p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">pancakes</span> <span style="color: blue;">=</span> bag <span style="color: #994;"><span style="font-weight: bold;">'</span>egg<span style="font-weight: bold;">'</span></span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>egg<span style="font-weight: bold;">'</span></span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>cup of milk<span style="font-weight: bold;">'</span></span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>cup of flour<span style="font-weight: bold;">'</span></span>;
<span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">omelette</span> <span style="color: blue;">=</span> bag <span style="color: #994;"><span style="font-weight: bold;">'</span>egg<span style="font-weight: bold;">'</span></span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>egg<span style="font-weight: bold;">'</span></span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>egg<span style="font-weight: bold;">'</span></span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>cup of milk<span style="font-weight: bold;">'</span></span>;
<span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">shopping-bag</span> <span style="color: blue;">=</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">pancakes</span> <span style="color: blue;">⊎</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">omelette</span> <span style="color: blue;">⊎</span> <span style="color: blue;">&lt;</span><span style="color: #994;">gum chocolate</span><span style="color: blue;">&gt;</span>;
<span style="color: #449;">say</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">shopping-bag</span>;
<span style="color: #999;"># bag(gum, cup of flour, egg(5), cup of milk(2), chocolate)</span></pre></div>
<p>We used two of our <code>Bag</code>s along with a 2-item list, which got correctly coerced for us, so we didn't have to do anything.</p>
<p>A more impressive operator is ≼ and it's mirror ≽, which tell whether a <a href="https://docs.perl6.org/type/Baggy"><code>Baggy</code></a> on the narrow side of the operator is a subset of the <code>Baggy</code> on the other side; meaning all the objects in the smaller <code>Baggy</code> are present in the larger one and their weights are at most as big.</p>
<p>Here's a challenge: we have some materials and some stuff we want to build. Problem is, we don't have enough materials to build <em>all</em> the stuff, so what we want to know is to find out what combinations of stuff we can build. Let's use some <code>Bag</code>s!</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">materials</span> <span style="color: blue;">=</span> bag <span style="color: #994;"><span style="font-weight: bold;">'</span>wood<span style="font-weight: bold;">'</span></span> <span style="color: blue;">xx</span> <span style="color: #449;">300</span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>glass<span style="font-weight: bold;">'</span></span> <span style="color: blue;">xx</span> <span style="color: #449;">100</span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>brick<span style="font-weight: bold;">'</span></span> <span style="color: blue;">xx</span> <span style="color: #449;">3000</span>;
<span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">@</span><span style="color: #440; font-weight: bold">wanted</span> <span style="color: blue;">=</span>
bag(<span style="color: #994;"><span style="font-weight: bold;">'</span>wood<span style="font-weight: bold;">'</span></span> <span style="color: blue;">xx</span> <span style="color: #449;">200</span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>glass<span style="font-weight: bold;">'</span></span> <span style="color: blue;">xx</span> <span style="color: #449;">50</span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>brick<span style="font-weight: bold;">'</span></span> <span style="color: blue;">xx</span> <span style="color: #449;">3000</span>) <span style="color: blue;">but</span> <span style="color: #994;"><span style="font-weight: bold;">'</span>house<span style="font-weight: bold;">'</span></span>,
bag(<span style="color: #994;"><span style="font-weight: bold;">'</span>wood<span style="font-weight: bold;">'</span></span> <span style="color: blue;">xx</span> <span style="color: #449;">100</span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>glass<span style="font-weight: bold;">'</span></span> <span style="color: blue;">xx</span> <span style="color: #449;">50</span>) <span style="color: blue;">but</span> <span style="color: #994;"><span style="font-weight: bold;">'</span>shed<span style="font-weight: bold;">'</span></span>,
bag(<span style="color: #994;"><span style="font-weight: bold;">'</span>wood<span style="font-weight: bold;">'</span></span> <span style="color: blue;">xx</span> <span style="color: #449;">50</span>) <span style="color: blue;">but</span> <span style="color: #994;"><span style="font-weight: bold;">'</span>dog-house<span style="font-weight: bold;">'</span></span>;
<span style="color: #449;">say</span> <span style="color: #994;"><span style="font-weight: bold;">'</span>We can build...<span style="font-weight: bold;">'</span></span>;
<span style="color: blue;">.</span><span style="color: #449;">put</span> <span style="color: blue;">for</span> <span style="color: #440; font-weight: bold">@</span><span style="color: #440; font-weight: bold">wanted</span><span style="color: blue;">.</span>combinations<span style="color: blue;">.</span><span style="color: #449;">grep</span><span style="color: blue;">:</span> { <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">materials</span> <span style="color: blue;">≽</span> [<span style="color: blue;">⊎</span>] <span style="color: blue;">|</span><span style="color: #440; font-weight: bold">$</span><span style="color: #449;">^</span><span style="color: #440; font-weight: bold">stuff-we-want</span> };
<span style="color: #999;"># OUTPUT:</span>
<span style="color: #999;"># We can build...</span>
<span style="color: #999;">#</span>
<span style="color: #999;"># house</span>
<span style="color: #999;"># shed</span>
<span style="color: #999;"># dog-house</span>
<span style="color: #999;"># house shed</span>
<span style="color: #999;"># house dog-house</span>
<span style="color: #999;"># shed dog-house</span></pre></div>
<p>The <code>$materials</code> is a <code>Bag</code> with our materials. We used <a href="https://docs.perl6.org/routine/xx"><code>xx</code> repetition operator</a> to indicate quantities of each. Then we have a <code>@wanted</code> <a href="https://docs.perl6.org/type/Array"><code>Array</code></a> with three <code>Bag</code>s in it: that's the stuff we want to build. We've also used used the <a href="https://docs.perl6.org/routine/but"><code>but</code></a> operator on them to mix in names for them to override what those bags will <code>.put</code> out as at the end.</p>
<p>Now for the interesting part! We call <a href="https://docs.perl6.org/routine/combinations"><code>.combinations</code></a> on the our list of stuff we want, and just as the name suggests, we get all the possible combinations of stuff we can build. Then, we <a href="https://docs.perl6.org/routine/grep"><code>.grep</code></a> over the result, looking for any combination that takes at most all of the materials we have (that's the <code>≽</code> operator). On it's fatter end, we have our <code>$materials</code> <code>Bag</code> and on its narrower end, we have the <code>⊎</code> operator that adds the bags of each combination of our stuff we want together, except we use it as a metaoperator <code>[⊎]</code>, which is the same as putting that operator between each item of <code>$^stuff-we-want</code>. In case you it's new to you: the <a href="https://docs.perl6.org/language/variables#index-entry-%24%5E"><code>$^</code> twigil</a> on <code>$^stuff-we-want</code> creates an implicit signature on our <code>.grep</code> block and we can name that variable anything we want.</p>
<p>And there we have it! The output of the program shows we can build any combination of stuff, except the one that contains all three items. I guess we just can't have it all...</p>
<p>...But wait! There's more!</p>
<h2><a id="user-content-mixing-it-up" class="anchor" href="#mixing-it-up" aria-hidden="true"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Mixing it Up</h2>
<p>Let's look back at our recipe code. There's something not quite perfect about it:</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">recipe</span> <span style="color: blue;">=</span> bag <span style="color: #994;"><span style="font-weight: bold;">'</span>egg<span style="font-weight: bold;">'</span></span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>egg<span style="font-weight: bold;">'</span></span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>cup of milk<span style="font-weight: bold;">'</span></span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>cup of flour<span style="font-weight: bold;">'</span></span>;
<span style="color: #449;">say</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">recipe</span>;
<span style="color: #999;"># OUTPUT: bag(cup of flour, egg(2), cup of milk)</span></pre></div>
<p>What if a recipe calls for half a cup of milk instead of a whole one? How do we represent a quarter of a teaspoon of salt, if <code>Bags</code> can only ever have integer weights?</p>
<p>The answer to that is the <a href="https://docs.perl6.org/type/Mix"><code>Mix</code> type</a> (with the corresponding mutable version, <a href="https://docs.perl6.org/type/MixHash"><code>MixHash</code></a>). Unlike a <code>Bag</code>, a <code>Mix</code> supports all <a href="https://docs.perl6.org/type/Real"><code>Real</code></a> weights, including negative weights. Thus, our recipe is best modeled with a <code>Mix</code>.</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">recipe</span> <span style="color: blue;">=</span> <span style="color: #449;">Mix</span><span style="color: blue;">.</span>new-from-pairs<span style="color: blue;">:</span> <span style="color: #994;"><span style="font-weight: bold;">'</span>egg<span style="font-weight: bold;">'</span></span> <span style="color: blue;">=&gt;</span> <span style="color: #449;">2</span>, <span style="color: #994;"><span style="font-weight: bold;">'</span>cup of milk<span style="font-weight: bold;">'</span></span> <span style="color: blue;">=&gt;</span> ½,
<span style="color: #994;"><span style="font-weight: bold;">'</span>cup of flour<span style="font-weight: bold;">'</span></span> <span style="color: blue;">=&gt;</span> ¾, <span style="color: #994;"><span style="font-weight: bold;">'</span>salt<span style="font-weight: bold;">'</span></span> <span style="color: blue;">=&gt;</span> ¼;
<span style="color: #449;">say</span> <span style="color: #440; font-weight: bold">$</span><span style="color: #440; font-weight: bold">recipe</span>;
<span style="color: #999;"># mix(salt(0.25), cup of flour(0.75), egg(2), cup of milk(0.5))</span></pre></div>
<p>Be sure to quote your keys and don't use colonpair form (<code>:42a</code>, or <code>:a(42)</code>), since those are treated as named arguments. There's also a <a href="https://docs.perl6.org/routine/mix"><code>mix</code> routine</a>, but it doesn't take weights and functions just like <code>bag</code> routine, except returning a <code>Mix</code>. And, of course, you can use a <code>.Mix</code> coercer on a hash or a list of pairs.</p>
<p>Less-Than-Awesome creation aside, let's make something with mixes! Say, you're an alchemist. You want to make a bunch of awesome potions and you need to know the total amount of ingredients you'll need. However, you realize that some of the ingredients needed by some reactions are actually produced as a byproduct by other reactions you're making. So, what's the most efficient amount of stuff you'll need? <code>Mix</code>es to the rescue!</p>
<div class="highlight highlight-source-perl6fe"><pre style="font-size: 14px; font-family: monospace"><span style="color: blue;">my</span> <span style="color: #440; font-weight: bold">%</span><span style="color: #440; font-weight: bold">supplies</span> <span style="color: blue;">=</span>
immortality-serum <span style="color: blue;">=&gt;</span>
(<span style="color: blue;">:</span>oxium(<span style="color: #449;">6.98</span>), <span style="color: blue;">:</span>morphics(<span style="color: #449;">123.3</span>), <span style="color: blue;">:</span>unobtainium(<span style="color: #449;">2</span>) )<span style="color: blue;">.</span><span style="color: #449;">Mix</span>,
invisibility-potion <span style="color: blue;">=&gt;</span>
(<span style="color: blue;">:</span>forma(<span style="color: #449;">9.85</span>), <span style="color: blue;">:</span>rubidium(<span style="color: #449;">56.3</span>), <span style="color: blue;">:</span>unobtainium(−0<span style="color: blue;">.</span>3) )<span style="color: blue;">.</span><span style="color: #449;">Mix</span>,
calming-potion <span style="color: blue;">=&gt;</span>
(<span style="color: blue;">:</span>forma(<span style="color: #449;">9.15</span>), <span style="color: blue;">:</span>rubidium(−30<span style="color: blue;">.</span>3), <span style="color: blue;">:</span>kuva(<span style="color: #449;">0.3</span>) )<span style="color: blue;">.</span><span style="color: #449;">Mix</span>,
potion-of-speed <span style="color: blue;">=&gt;</span>
(<span style="color: blue;">:</span>forma(<span style="color: #449;">1.35</span>), <span style="color: blue;">:</span>nano-spores(<span style="color: #449;">1.3</span>), <span style="color: blue;">:</span>kuva(<span style="color: #449;">1.3</span>), <span style="color: blue;">:</span>elf-blood(<span style="color: #449;">4</span>))<span style="color: blue;">.</span><span style="color: #449;">Mix</span>;
<span style="color: #449;">say</span> [<span style="color: blue;">⊎</span>] <span style="color: #440; font-weight: bold">%</span><span style="color: #440; font-weight: bold">supplies</span><span style="color: blue;">.</span><span style="color: #449;">values</span>;
<span style="color: #999;"># OUTPUT: mix(unobtainium(1.7), elf-blood(4), morphics(123.3),</span>
<span style="color: #999;"># nano-spores(1.3), forma(20.35), oxium(6.98), rubidium(26), kuva(1.6))</span></pre></div>
<p>For convenience, we set up a <a href="https://docs.perl6.org/type/Hash"><code>Hash</code></a>, with keys being names of potions and values being <code>Mix</code>es with quantities of ingredients. For reactions that produce one of the ingredients we seek, we've used negative weights, indicating the amount produced.</p>
<p>Then, we used the same <code>⊎</code> set addition operator we saw earlier, in it's meta
form: <code>[⊎]</code>. We supply it the <code>.values</code> of our <code>Hash</code> that are our <code>Mix</code>es, and it happily adds up all of our ingredients, which we see in the output.</p>
<p>Look at <code>unobtainium</code> and <code>rubidium</code>: the set operator correctly accounted for the quantities produced by reactions where those ingredients have negative weights!</p>
<p>With immortality serum successfully mixed, all we need to do now is figure out what to do for the next few millennia... How about coding some Perl?</p>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment