-
-
Notifications
You must be signed in to change notification settings - Fork 655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nucleotide-count: add generator and update example #1014
Changes from 1 commit
7ecac4c
9670930
542adcc
a3dffd7
27a70ae
8894405
e26219f
bed1582
de1947d
4de30f4
e20414e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"sort" | ||
"strconv" | ||
"strings" | ||
"text/template" | ||
|
||
"../../../gen" | ||
) | ||
|
||
func main() { | ||
t, err := template.New("").Parse(tmpl) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
var j js | ||
if err := gen.Gen("nucleotide-count", &j, t); err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
|
||
// The JSON structure we expect to be able to unmarshal into | ||
type js struct { | ||
exercise string | ||
version string | ||
Cases []struct { | ||
Description string | ||
Cases []OneCase | ||
} | ||
} | ||
|
||
// OneCase represents each test case | ||
type OneCase struct { | ||
Description string | ||
Property string | ||
Strand string | ||
Expected map[string]interface{} | ||
} | ||
|
||
// ErrorExpected returns true if an error should be raised | ||
func (c OneCase) ErrorExpected() bool { | ||
_, exists := c.Expected["error"] | ||
return exists | ||
} | ||
|
||
// SortedMapString collects key:values for a map in sorted order | ||
func (c OneCase) SortedMapString() string { | ||
strs := make([]string, 0, len(c.Expected)) | ||
for k, v := range c.Expected { | ||
switch t := v.(type) { | ||
case float64: | ||
strs = append(strs, `"`+k+`": `+strconv.FormatFloat(t, 'f', -1, 64)) | ||
default: | ||
} | ||
|
||
} | ||
sort.Strings(strs) | ||
return strings.Join(strs, ",") | ||
} | ||
|
||
// template applied to above data structure generates the Go test cases | ||
var tmpl = `package dna | ||
|
||
{{.Header}} | ||
|
||
{{range .J.Cases}}// {{.Description}} | ||
var testCases = []struct { | ||
description string | ||
strand string | ||
expected map[string]int | ||
errorExpected bool | ||
}{ | ||
{{range .Cases}}{ | ||
description: {{printf "%q" .Description}}, | ||
strand: {{printf "%#v" .Strand}}, | ||
{{if .ErrorExpected}}errorExpected: true, | ||
{{else}}expected: map[string]int{ {{.SortedMapString}} }, | ||
{{- end}} | ||
}, | ||
{{end}}{{end}} | ||
} | ||
` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package dna | ||
|
||
// Source: exercism/problem-specifications | ||
// Commit: bbdcaee nucleotide-count: fix wrong order introduced in #951 | ||
// Problem Specifications Version: 1.2.0 | ||
|
||
// count all nucleotides in a strand | ||
var testCases = []struct { | ||
description string | ||
strand string | ||
expected map[string]int | ||
errorExpected bool | ||
}{ | ||
{ | ||
description: "empty strand", | ||
strand: "", | ||
expected: map[string]int{"A": 0, "C": 0, "G": 0, "T": 0}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be easy enough to use this expected type ?
and then generate this as
A nucleotide is one single letter after all. Perhaps too nit-picky, but it seems to match the exercise problem better. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems reasonable to me. Always a fan of more content in hints.md as well. |
||
}, | ||
{ | ||
description: "can count one nucleotide in single-character input", | ||
strand: "G", | ||
expected: map[string]int{"A": 0, "C": 0, "G": 1, "T": 0}, | ||
}, | ||
{ | ||
description: "strand with repeated nucleotide", | ||
strand: "GGGGGGG", | ||
expected: map[string]int{"A": 0, "C": 0, "G": 7, "T": 0}, | ||
}, | ||
{ | ||
description: "strand with multiple nucleotides", | ||
strand: "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC", | ||
expected: map[string]int{"A": 20, "C": 12, "G": 17, "T": 21}, | ||
}, | ||
{ | ||
description: "strand with invalid nucleotides", | ||
strand: "AGXXACT", | ||
errorExpected: true, | ||
}, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,35 +5,42 @@ import ( | |
"strings" | ||
) | ||
|
||
// Histogram is a mapping from nucleotide to its count in given DNA | ||
type Histogram map[byte]int | ||
|
||
// DNA is a list of nucleotides | ||
type DNA string | ||
|
||
const validNucleotides = "ACGT" | ||
var validNucleotides = []string{"A", "C", "G", "T"} | ||
|
||
func isValidNucleotide(nucleotide string) bool { | ||
for _, n := range validNucleotides { | ||
if nucleotide == n { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
// Count counts number of occurrences of given nucleotide in given DNA | ||
func (dna DNA) Count(nucleotide byte) (count int, err error) { | ||
if !strings.Contains(validNucleotides, string(nucleotide)) { | ||
func (dna DNA) Count(nucleotide string) (count int, err error) { | ||
if !isValidNucleotide(nucleotide) { | ||
return 0, errors.New("dna: invalid nucleotide " + string(nucleotide)) | ||
} | ||
|
||
return strings.Count(string(dna), string(nucleotide)), nil | ||
return strings.Count(string(dna), nucleotide), nil | ||
} | ||
|
||
// Counts generates a histogram of valid nucleotides in given DNA. | ||
// Returns error if DNA contains invalid nucleotide. | ||
func (dna DNA) Counts() (Histogram, error) { | ||
func (dna DNA) Counts() (result map[string]int, e error) { | ||
var total int | ||
h := Histogram{} | ||
result = make(map[string]int) | ||
|
||
for i := range validNucleotides { | ||
nucleotide := validNucleotides[i] | ||
h[nucleotide], _ = dna.Count(nucleotide) | ||
total += h[nucleotide] | ||
result[nucleotide], _ = dna.Count(nucleotide) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps replace
with
Then var total is unnecessary as well as the later if test using it; it also preserves the specific error from Count. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would fail the test with invalid nucleotides, like "AGXXACT" in the test suite... Because the algorithm only iterates on valid ones "ACGT". I'll remove pointless code :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see now. |
||
total += result[nucleotide] | ||
} | ||
if total != len(dna) { | ||
return nil, errors.New("dna: contains invalid nucleotide") | ||
} | ||
return h, nil | ||
return result, nil | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah,
That line is unnecessary. Can remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok .. I made it a little more useful :)
in the meantime the problem specification json structure has changed, so I updated also the generator