lpvm

lpvm

Some beginner questions regarding the Nucleotide problem of exercism.io

I’ve read the Basics of elixirschool only, so I have some questions regarding the Nucleotide problem of exercism.io.

Please take a look at https://paste.ofcode.org/37ZCKNYPTJbpFwcVLdMYRan

  1. What’s the purpose of @nucleotides under the defmodule declaration?
  2. Why does that tag mention A, C, G, T preceded by a question mark?
  3. What is wrong, or how to correct my counthelper implementation?
  4. What is the best source where I can have a more detailed esplanation than in elixirschool (i.e.) with examples?

Marked As Solved

peerreynders

peerreynders

https://elixir-lang.org/getting-started/module-attributes.html#as-constants

  • Why does that tag mention A, C, G, T preceded by a question mark?

https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html#utf-8-and-unicode

i.e.

  • What is the best source where I can have a more detailed explanation than in elixirschool (i.e.) with examples?

The “best” varies from person to person. But I don’t think you can go wrong with

First pass:

# file: nucleotide_count.exs
defmodule NucleotideCount do

  def counthelper(c, strand, nucleotide) do
    [hd | tl] = strand
    cond do
      hd == nucleotide -> counthelper(c + 1, tl, nucleotide)
      length(tl) != 0 -> counthelper(c, tl, nucleotide)        # 2.) Need (length tl) or length(tl)
      true -> c
    end
  end

  def count(strand, nucleotide) do
    counthelper(0, strand, nucleotide)
  end
end # 1.) added missing module "end"

IO.inspect(NucleotideCount.count('AATAA', ?A))
IO.inspect(NucleotideCount.count('AATAA', ?T))

But still:

$ elixir nucleotide_count.exs
** (MatchError) no match of right hand side value: []
    nucleotide_count.exs:5: NucleotideCount.counthelper/3
    nucleotide_count.exs:18: (file)
    (elixir) lib/code.ex:677: Code.require_file/2
$

i.e. [hd | tl] = strand the match fails when the list is empty - the code never gets to length(tl) != 0

quick fix: Pattern Matching

# file: nucleotide_count.exs
defmodule NucleotideCount do

  def counthelper(c, [], _) do
    c # ran out of strand - return count
  end
  def counthelper(c, strand, nucleotide) do
    [hd | tl] = strand
    cond do
      hd == nucleotide -> counthelper(c + 1, tl, nucleotide)
      length(tl) != 0 -> counthelper(c, tl, nucleotide)
      true -> c
    end
  end

  def count(strand, nucleotide) do
    counthelper(0, strand, nucleotide)
  end
end

IO.inspect(NucleotideCount.count('AATAA', ?A))
IO.inspect(NucleotideCount.count('AATAA', ?T))
$ elixir nucleotide_count.exs
4
1
$

But we can do better …

# file: nucleotide_count.exs
defmodule NucleotideCount do

  def counthelper(c, [], _),
    do: c                                                        # ran out of strand - return count
  def counthelper(c, [hd|tl], nucleotide) when hd == nucleotide, # 1. move pattern match to function head
    do: counthelper(c + 1, tl, nucleotide)                       # 2. extend pattern with a guard
  def counthelper(c, [_|tl], nucleotide),                        # 3. here we know "hd" isn't the "nucleotide"
    do: counthelper(c, tl, nucleotide)                           # we don't care about the "hd" so we use "_" instead

  def count(strand, nucleotide),
    do: counthelper(0, strand, nucleotide)

end

IO.inspect(NucleotideCount.count('AATAA', ?A))
IO.inspect(NucleotideCount.count('AATAA', ?T))

guards
named functions and do/end blocks

The whole thing may look a bit more familiar this way:

# file: nucleotide_count.exs
defmodule NucleotideCount do

  def counthelper(c, strand, nucleotide) do
    case strand do
      [x|xs] when x == nucleotide ->
        counthelper(c+1,xs,nucleotide)
      [_|xs] ->
        counthelper(c,xs,nucleotide)
      _ ->
        c
    end
  end

  def count(strand, nucleotide),
    do: counthelper(0, strand, nucleotide)

end

IO.inspect(NucleotideCount.count('AATAA', ?A))
IO.inspect(NucleotideCount.count('AATAA', ?T))

And then there is

# file: nucleotide_count.exs
defmodule NucleotideCount do

  def count(strand, nucleotide) do
    n_accumulate = fn
      x,c when x == nucleotide -> c + 1
      _,c -> c
    end
    List.foldl(strand, 0, n_accumulate)
  end

end

IO.inspect(NucleotideCount.count('AATAA', ?A))
IO.inspect(NucleotideCount.count('AATAA', ?T))

List.foldl/3
Enum.reduce/3

Defining Multiple Clauses In An Anonymous Function

Also Liked

peerreynders

peerreynders

IO.inspect(Kernel.is_binary("string"))   # true
IO.inspect(Kernel.is_list("string"))     # false
IO.inspect(Kernel.is_binary('charlist')) # false
IO.inspect(Kernel.is_list('charlist'))   # true
IO.inspect(Kernel.is_list('A'))          # true
IO.inspect(Kernel.is_integer('A'))       # false
IO.inspect(Kernel.is_list(?A))           # false
IO.inspect(Kernel.is_integer(?A))        # true
kokolegorille

kokolegorille

Because ?A is 65 while ‘A’ is [65]

kokolegorille

kokolegorille

Maybe You can replace cond with function head pattern matching.

defmodule NucleotideCount do
  @nucleotides [?A, ?C, ?G, ?T]
  
  def counthelper(c, [], _nucleotide), do: c
  def counthelper(c, [head], nucleotide) when head == nucleotide, do: c + 1
  def counthelper(c, [head], nucleotide) when head != nucleotide, do: c
  def counthelper(c, [head | tail], nucleotide) when head == nucleotide do 
    counthelper(c + 1, tail, nucleotide)
  end
  def counthelper(c, [head | tail], nucleotide) when head != nucleotide do 
    counthelper(c, tail, nucleotide)
  end
  
  def count(strand, nucleotide) do
    counthelper(0, strand, nucleotide)
  end
end

iex(1)> IO.inspect(NucleotideCount.count('AATAA', ?A))
4
4
iex(2)> IO.inspect(NucleotideCount.count('AATAA', ?T))
1
1

@nucleotides is not used, You might add some guard clause like this.

def counthelper(_c, [head | _tail], _nucleotide) when head not in @nucleotides, do: raise "error"

Where Next?

Popular in Chat/Questions Top

armanm
I know zero downtime deployment can mean different things depending on your application and what your users can tolerate so expectations ...
New
Yoga
Or at least, in the works? All I can find are bits and pieces but not a single project from start to finish. Including things like i18n,...
New
eliottramirez
Hello, I’m trying to learn Phoenix but I constantly find difficult understanding how the framework works, and I think part of this is th...
New
ericdouglas
I think that would be really interesting to have official books created by the community about all kinds of development we can do with El...
New
woohaaha
I’m coming from Ruby and Rails. I have read some Elixir and Phoenix books. They shed a lot of light about building applications in Elixir...
New
Nopp
Hello there, i have a lot to read and to learn, but are there some books, that are focussing on how i “should” plan the architecture of ...
New
pillaiindu
I am a VSCode and Sublime user and I know VIM, though I don't use VIM directly but whenever I code inside Sublime or VSCode, I use Vim em...
New
Santheepkumar
Hi all, I am a Fullstack JS developer for last 2 years. I need a good guide to learn elixer. Any suggestions please
New
Fl4m3Ph03n1x
GenStage and Flow resources? I have been hearing about GenStage and Flow, a tool built upon it, for quite some time. I understand it allo...
New
marciol
Hey, I have very restricted resources and time so I’m trying to understand the best way to learn Liveview in terms of cost/time. The Pra...
New

Other popular topics Top

yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New

We're in Beta

About us Mission Statement