Skip to content

Commit

Permalink
Add nucleotide-count (#111)
Browse files Browse the repository at this point in the history
* Convert json objects to roc records and still output generated tests when roc format fails
* Add nucleotide-count
* use single pass and remove crash

---------

Co-authored-by: Aurélien Geron <ageron@users.noreply.github.com>
  • Loading branch information
isaacvando and ageron authored Sep 23, 2024
1 parent a1811aa commit 6ef7943
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 1 deletion.
14 changes: 13 additions & 1 deletion bin/generate_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@ def to_roc_tuple(values: Any):
list_content = ", ".join([to_roc(v) for v in tuple(values)])
return f"({list_content})"

def to_roc_record(obj: Dict[str, Any]):
items = []
for key, value in obj.items():
camel_key = to_camel(key)
roc_value = to_roc(value)
items.append(f"{camel_key}: {roc_value}")

return "{ " + ", ".join(items) + " }"


def to_roc_bool(value: bool):
return "Bool.true" if value else "Bool.false"
Expand All @@ -193,6 +202,8 @@ def to_roc(value: Any) -> str:
return to_roc_list(value)
elif isinstance(value, tuple):
return to_roc_tuple(value)
elif isinstance(value, dict):
return to_roc_record(value)
elif value is None:
return "{}"
else:
Expand Down Expand Up @@ -481,7 +492,7 @@ def generate_exercise(
logger.debug(f"{slug}: formatting tmp file {tmpfile}")
format_file(tmpfile)
except subprocess.CalledProcessError as e:
return False
pass

if check:
return check_template(slug, tests_path, tmpfile)
Expand Down Expand Up @@ -545,6 +556,7 @@ def generate(
env.filters["to_roc_bool"] = to_roc_bool
env.filters["to_roc_list"] = to_roc_list
env.filters["to_roc_tuple"] = to_roc_tuple
env.filters["to_roc_record"] = to_roc_record
env.filters["wrap_overlong"] = wrap_overlong
env.filters["regex_replace"] = regex_replace
env.filters["regex_find"] = regex_find
Expand Down
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@
"prerequisites": [],
"difficulty": 2
},
{
"slug": "nucleotide-count",
"name": "Nucleotide Count",
"uuid": "9f07740b-eb8a-45f9-a137-e2930a224815",
"practices": [],
"prerequisites": [],
"difficulty": 2
},
{
"slug": "rna-transcription",
"name": "RNA Transcription",
Expand Down
23 changes: 23 additions & 0 deletions exercises/practice/nucleotide-count/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Instructions

Each of us inherits from our biological parents a set of chemical instructions known as DNA that influence how our bodies are constructed.
All known life depends on DNA!

> Note: You do not need to understand anything about nucleotides or DNA to complete this exercise.
DNA is a long chain of other chemicals and the most important are the four nucleotides, adenine, cytosine, guanine and thymine.
A single DNA chain can contain billions of these four nucleotides and the order in which they occur is important!
We call the order of these nucleotides in a bit of DNA a "DNA sequence".

We represent a DNA sequence as an ordered collection of these four nucleotides and a common way to do that is with a string of characters such as "ATTACG" for a DNA sequence of 6 nucleotides.
'A' for adenine, 'C' for cytosine, 'G' for guanine, and 'T' for thymine.

Given a string representing a DNA sequence, count how many of each nucleotide is present.
If the string contains characters that aren't A, C, G, or T then it is invalid and you should signal an error.

For example:

```text
"GATTACA" -> 'A': 3, 'C': 1, 'G': 1, 'T': 2
"INVALID" -> error
```
12 changes: 12 additions & 0 deletions exercises/practice/nucleotide-count/.meta/Example.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module [nucleotideCounts]

nucleotideCounts : Str -> Result { a : U64, c : U64, g : U64, t : U64 } _
nucleotideCounts = \input ->
Str.toUtf8 input
|> List.walkTry { a: 0, c: 0, g: 0, t: 0 } \state, elem ->
when elem is
'A' -> Ok { state & a: state.a + 1 }
'C' -> Ok { state & c: state.c + 1 }
'G' -> Ok { state & g: state.g + 1 }
'T' -> Ok { state & t: state.t + 1 }
_ -> Err (InvalidNucleotide elem)
19 changes: 19 additions & 0 deletions exercises/practice/nucleotide-count/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"isaacvando"
],
"files": {
"solution": [
"NucleotideCount.roc"
],
"test": [
"nucleotide-count-test.roc"
],
"example": [
".meta/Example.roc"
]
},
"blurb": "Given a DNA string, compute how many times each nucleotide occurs in the string.",
"source": "The Calculating DNA Nucleotides_problem at Rosalind",
"source_url": "https://rosalind.info/problems/dna/"
}
17 changes: 17 additions & 0 deletions exercises/practice/nucleotide-count/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{%- import "generator_macros.j2" as macros with context -%}
{{ macros.canonical_ref() }}
{{ macros.header() }}

import {{ exercise | to_pascal }} exposing [{{ cases[0]["property"] | to_camel }}]

{% for case in cases -%}
# {{ case["description"] }}
expect
result = {{ case["property"] | to_camel }} {{ case["input"]["strand"] | to_roc }}
{%- if case["expected"]["error"] %}
Result.isErr result
{%- else %}
result == Ok {{ case["expected"] | to_roc }}
{%- endif %}

{% endfor %}
25 changes: 25 additions & 0 deletions exercises/practice/nucleotide-count/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[3e5c30a8-87e2-4845-a815-a49671ade970]
description = "empty strand"

[a0ea42a6-06d9-4ac6-828c-7ccaccf98fec]
description = "can count one nucleotide in single-character input"

[eca0d565-ed8c-43e7-9033-6cefbf5115b5]
description = "strand with repeated nucleotide"

[40a45eac-c83f-4740-901a-20b22d15a39f]
description = "strand with multiple nucleotides"

[b4c47851-ee9e-4b0a-be70-a86e343bd851]
description = "strand with invalid nucleotides"
5 changes: 5 additions & 0 deletions exercises/practice/nucleotide-count/NucleotideCount.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module [nucleotideCounts]

nucleotideCounts : Str -> Result { a : U64, c : U64, g : U64, t : U64 } _
nucleotideCounts = \input ->
crash "Please implement 'nucleotideCounts'"
37 changes: 37 additions & 0 deletions exercises/practice/nucleotide-count/nucleotide-count-test.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# These tests are auto-generated with test data from:
# https://github.com/exercism/problem-specifications/tree/main/exercises/nucleotide-count/canonical-data.json
# File last updated on 2024-09-22
app [main] {
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
}

main =
Task.ok {}

import NucleotideCount exposing [nucleotideCounts]

# empty strand
expect
result = nucleotideCounts ""
result == Ok { a: 0, c: 0, g: 0, t: 0 }

# can count one nucleotide in single-character input
expect
result = nucleotideCounts "G"
result == Ok { a: 0, c: 0, g: 1, t: 0 }

# strand with repeated nucleotide
expect
result = nucleotideCounts "GGGGGGG"
result == Ok { a: 0, c: 0, g: 7, t: 0 }

# strand with multiple nucleotides
expect
result = nucleotideCounts "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC"
result == Ok { a: 20, c: 12, g: 17, t: 21 }

# strand with invalid nucleotides
expect
result = nucleotideCounts "AGXXACT"
Result.isErr result

0 comments on commit 6ef7943

Please sign in to comment.