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
- What’s the purpose of @nucleotides under the
defmoduledeclaration? - Why does that tag mention A, C, G, T preceded by a question mark?
- What is wrong, or how to correct my counthelper implementation?
- What is the best source where I can have a more detailed esplanation than in elixirschool (i.e.) with examples?
Marked As Solved
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
- Elixir - Getting Started Pages (1-23) and then
- Elixir - MIX AND OTP (1-10)
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))
Also Liked
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
Because ?A is 65 while ‘A’ is [65]
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"







