avatarThe Pragmatic Programmers

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

3480

Abstract

After you’ve successfully pulled down the credo package, you’ll be able to use the credo mix tasks. To do that, run the following in the base directory of your genetic framework:</p><div id="e699"><pre>​ ​$ ​​mix​​ ​​credo​​ ​​<span class="hljs-comment">--strict​</span> ​ ​...​ ​ Analysis took <span class="hljs-number">0.07</span> <span class="hljs-built_in">seconds</span> (<span class="hljs-number">0.00</span>s <span class="hljs-built_in">to</span> <span class="hljs-built_in">load</span>, <span class="hljs-number">0.07</span>s running <span class="hljs-number">54</span> checks <span class="hljs-keyword">on</span> <span class="hljs-title">12</span> <span class="hljs-title">files</span>) ​ <span class="hljs-number">55</span> mods/funs, found <span class="hljs-number">1</span> refactoring opportunity, <span class="hljs-number">10</span> code readability issues.</pre></div><p id="38b3">The task might take awhile to run. Once it’s complete, you’ll see some output related to the issues credo finds with your project. In this example, all of your modules will be tagged for not including a @moduledoc tag. That’s OK; you can add documentation later. credo also identified a refactoring opportunity in toolbox/selection.exs. This is because the function body of Toolbox.Selection.roulette/2 is nested too deeply. As a general rule, functions should only do one thing — the level of nesting in your function is usually an indication that your function is doing more than it’s supposed to. The warning is telling you that you could possibly break the function into smaller pieces that would make your code cleaner and easier to test.</p><p id="6784">Take a look at the function Toolbox.Selection.roulette/2 in selection.ex:</p><div id="3416"><pre>​ ​def​ roulette(chromosomes, n) ​<span class="hljs-built_in">do</span>​ ​ sum_fitness = ​ chromosomes ​ |<span class="hljs-type">> Enum</span>.map(& &<span class="hljs-number">1.</span>fitness) ​ |<span class="hljs-type">> Enum</span>.<span class="hljs-built_in">sum</span>() ​ ​ <span class="hljs-number">0.</span>.(n - <span class="hljs-number">1</span>) ​ |<span class="hljs-type">> Enum</span>.map(​fn​ <span class="hljs-keyword">_</span> -> ​ u = ​:rand​.uniform() * sum_fitness ​ ​ chromosomes ​ |<span class="hljs-type">> Enum</span>.reduce_while( ​ <span class="hljs-number">0</span>, ​ ​fn​ x, <span class="hljs-built_in">sum</span> -> ​ ​<span class="hljs-keyword">if</span>​ x.fitness + <span class="hljs-built_in">sum</span> > u ​<span class="hljs-built_in">do</span>​ ​ {​:halt​, x} ​ ​<span class="hljs-keyword">else</span>​ ​ {​:cont​, x.fitness + <span class="hljs-built_in">sum</span>} ​ ​<span class="hljs-keyword">end</span>​ ​ ​<span class="hljs-keyword">end</span>​ ​ ) ​ ​<span class="hljs-keyword">end</span>​) ​ ​<span class="hljs-keyword">end</span></pre></div><p id="5da0">You defined this function in Chapter 5, <a href="https://readmedium.com/chapter-5-selecting-the-best-cb1627e4a287"><i>Selecting the Best</i></a>. Remember, roulette selection simulates the spinning of a roulette wheel to select chromosomes where each chromosome occupies a percentage of the wheel based on its fitness. The offending part of the function occurs in the body of the anonymous function in Enum.reduce_while/3. The function is meant to simulate the spinning of a roulette wheel. To

Options

fix it, replace the call to Enum.reduce_while/3 with a call to a private function spin/2, like this:</p><div id="b24c"><pre>​ ​def​ roulette(chromosomes, n) ​<span class="hljs-built_in">do</span>​ ​ sum_fitness = ​ chromosomes ​ |<span class="hljs-type">> Enum</span>.map(& &<span class="hljs-number">1.</span>fitness) ​ |<span class="hljs-type">> Enum</span>.<span class="hljs-built_in">sum</span>() ​ ​ <span class="hljs-number">0.</span>.(n - <span class="hljs-number">1</span>) ​ |<span class="hljs-type">> Enum</span>.map(​fn​ <span class="hljs-keyword">_</span> -> ​ u = ​:rand​.uniform() * sum_fitness ​ spin(chromosomes, u) ​ ​<span class="hljs-keyword">end</span>​) ​ ​<span class="hljs-keyword">end</span></pre></div><p id="4df1">Then implement spin/2 like this:</p><div id="66d9"><pre>​ ​defp​ spin(chromosomes, u) ​<span class="hljs-built_in">do</span>​ ​ chromosomes ​ |<span class="hljs-type">> Enum</span>.reduce_while( ​ <span class="hljs-number">0</span>, ​ ​fn​ x, <span class="hljs-built_in">sum</span> -> ​ ​<span class="hljs-keyword">if</span>​ x.fitness + <span class="hljs-built_in">sum</span> > u ​<span class="hljs-built_in">do</span>​ ​ {​:halt​, x} ​ ​<span class="hljs-keyword">else</span>​ ​ {​:cont​, x.fitness + <span class="hljs-built_in">sum</span>} ​ ​<span class="hljs-keyword">end</span>​ ​ ​<span class="hljs-keyword">end</span>​ ​ ) ​ ​<span class="hljs-keyword">end</span></pre></div><p id="17bf">Notice you’ve broken your code down into separate functions that are easier to read and, in theory, easier to work with. Now if you run mix credo again, the warning should disappear:</p><div id="6905"><pre>​ ​$ ​​mix​​ ​​credo​​ ​​<span class="hljs-comment">--strict​</span> ​ ​...​ ​ Analysis took <span class="hljs-number">0.1</span> <span class="hljs-built_in">seconds</span> (<span class="hljs-number">0.00</span>s <span class="hljs-built_in">to</span> <span class="hljs-built_in">load</span>, <span class="hljs-number">0.1</span>s running <span class="hljs-number">54</span> checks <span class="hljs-keyword">on</span> <span class="hljs-title">13</span> <span class="hljs-title">files</span>) ​ <span class="hljs-number">58</span> mods/funs, found <span class="hljs-number">10</span> code readability issues.</pre></div><p id="0f6f">Again, this step isn’t entirely necessary, but it can help you identify possible issues with your code and clean up your code so it’s easier for you and others to work with. You should perform checks with credo as you develop.</p><p id="2eea"><i>👈 <a href="https://readmedium.com/writing-property-tests-with-exunit-1ba6d1e0d1e3">Writing Property Tests with ExUnit</a> | <a href="https://readmedium.com/table-of-contents-879fc8614df">TOC</a> | <a href="https://readmedium.com/writing-type-specifications-c0224f5d407c">Writing Type Specifications</a> 👉</i></p><p id="809f"><i>Genetic Algorithms in Elixir by Sean Moriarity can be purchased in other book formats <a href="https://pragprog.com/titles/smgaelixir">directly from the Pragmatic Programmers</a>. If you notice a code error or formatting mistake, please let us know <a href="https://readmedium.com/how-to-report-errata-4e164674347a">here</a> so that we can fix it.</i></p><figure id="9656"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*U2E2a23SuM4520w9CUXSWw.jpeg"><figcaption></figcaption></figure></article></body>

Cleaning Up Your Framework

Genetic Algorithms in Elixir — by Sean Moriarity (90 / 101)

👈 Writing Property Tests with ExUnit | TOC | Writing Type Specifications 👉

Part of good development practice is making sure your code is consistent, clean, and concise. Keeping your code clean and consistent is important to the long-term maintenance of your framework. You, and any future developers who improve your code base, will benefit by ensuring your code is easy to build upon in the future.

In this section, you’ll use two tools: credo and the Elixir formatter to improve the structure and style of your code.

Using the Elixir Formatter

Following the coding style of a particular programming language is important to ensuring your code is readable to you and any other developers that work on your project. Fortunately, Elixir comes with a formatter that will help you enforce the formatting standards of the language.

If you take a look at your framework, the genetic directory should contain a file .formatter.exs. This file contains configurations for the mix format task. It looks like this:

​ ​# Used by "mix format"​
​ [
​   ​inputs:​ [​"​​{mix,.formatter}.exs"​, ​"​​{config,lib,test}/**/*.{ex,exs}"​]
​ ]

You can configure the formatter from this file; however, you don’t need to for your framework. You just need to run mix format in the terminal in your genetic directory, like this:

​ ​$ ​​mix​​ ​​format​

The formatter will run for a bit and then exit without any output. The formatter ensures your code meets the Elixir Style Guide.[26] For the most part, your code shouldn’t be much different, minus a few additional newlines here or there.

This step isn’t entirely necessary, but it’s beneficial to the readability of your code and your development moving forward.

Using Credo

credo[27] is a static code analysis tool for Elixir. It will analyze your code for you and offer suggestions for refactoring, improving readability, and avoiding warnings. You’ll run credo and address some minor issues in your framework.

To get started, add credo to your dependencies:

​ {​:credo​, ​"​​~> 1.4"​, ​only:​ [​:dev​, ​:test​], ​runtime:false}

You only need credo during development and testing, so you specify that here. The runtime: false option tells Elixir that credo doesn’t need to be compiled at runtime.

Now, run mix deps.get. After you’ve successfully pulled down the credo package, you’ll be able to use the credo mix tasks. To do that, run the following in the base directory of your genetic framework:

​ ​$ ​​mix​​ ​​credo​​ ​​--strict​
​ ​...​
​ Analysis took 0.07 seconds (0.00s to load, 0.07s running 54 checks on 12 files)
​ 55 mods/funs, found 1 refactoring opportunity, 10 code readability issues.

The task might take awhile to run. Once it’s complete, you’ll see some output related to the issues credo finds with your project. In this example, all of your modules will be tagged for not including a @moduledoc tag. That’s OK; you can add documentation later. credo also identified a refactoring opportunity in toolbox/selection.exs. This is because the function body of Toolbox.Selection.roulette/2 is nested too deeply. As a general rule, functions should only do one thing — the level of nesting in your function is usually an indication that your function is doing more than it’s supposed to. The warning is telling you that you could possibly break the function into smaller pieces that would make your code cleaner and easier to test.

Take a look at the function Toolbox.Selection.roulette/2 in selection.ex:

​ ​def​ roulette(chromosomes, n) ​do​
​   sum_fitness =
​     chromosomes
​     |> Enum.map(& &1.fitness)
​     |> Enum.sum()
​ 
​   0..(n - 1)
​   |> Enum.map(​fn​ _ ->
​     u = ​:rand​.uniform() * sum_fitness
​ 
​     chromosomes
​     |> Enum.reduce_while(
​       0,
​       ​fn​ x, sum ->
​         ​if​ x.fitness + sum > u ​do​
​           {​:halt​, x}
​         ​else​
​           {​:cont​, x.fitness + sum}
​         ​end​
​       ​end​
​     )
​   ​end​)
​ ​end

You defined this function in Chapter 5, Selecting the Best. Remember, roulette selection simulates the spinning of a roulette wheel to select chromosomes where each chromosome occupies a percentage of the wheel based on its fitness. The offending part of the function occurs in the body of the anonymous function in Enum.reduce_while/3. The function is meant to simulate the spinning of a roulette wheel. To fix it, replace the call to Enum.reduce_while/3 with a call to a private function spin/2, like this:

​ ​def​ roulette(chromosomes, n) ​do​
​   sum_fitness =
​     chromosomes
​     |> Enum.map(& &1.fitness)
​     |> Enum.sum()
​ 
​   0..(n - 1)
​   |> Enum.map(​fn​ _ ->
​     u = ​:rand​.uniform() * sum_fitness
​     spin(chromosomes, u)
​   ​end​)
​ ​end

Then implement spin/2 like this:

​ ​defp​ spin(chromosomes, u) ​do​
​   chromosomes
​   |> Enum.reduce_while(
​     0,
​     ​fn​ x, sum ->
​       ​if​ x.fitness + sum > u ​do​
​         {​:halt​, x}
​       ​else​
​         {​:cont​, x.fitness + sum}
​       ​end​
​     ​end​
​   )
​ ​end

Notice you’ve broken your code down into separate functions that are easier to read and, in theory, easier to work with. Now if you run mix credo again, the warning should disappear:

​ ​$ ​​mix​​ ​​credo​​ ​​--strict​
​ ​...​
​ Analysis took 0.1 seconds (0.00s to load, 0.1s running 54 checks on 13 files)
​ 58 mods/funs, found 10 code readability issues.

Again, this step isn’t entirely necessary, but it can help you identify possible issues with your code and clean up your code so it’s easier for you and others to work with. You should perform checks with credo as you develop.

👈 Writing Property Tests with ExUnit | TOC | Writing Type Specifications 👉

Genetic Algorithms in Elixir by Sean Moriarity can be purchased in other book formats directly from the Pragmatic Programmers. If you notice a code error or formatting mistake, please let us know here so that we can fix it.

Smgaelixir
Recommended from ReadMedium