patternMinor
Rosalind's 3rd problem in Scheme
Viewed 0 times
problemrosalindscheme3rd
Problem
I have an imperative programming background and I've decided to study functional programming by applying it to problems found on sites such as Project Euler and Rosalind. My language of choice is Scheme (I use CHICKEN). As can be seen, I'm only starting out.
This is about the 3rd problem found on Rosalind, that goes like this:
In DNA strings, symbols 'A' and 'T' are complements of each other, as
are 'C' and 'G'.
The reverse complement of a DNA string s is the string sc formed by
reversing the symbols of s, then taking the complement of each symbol
(e.g., the reverse complement of "GTCA" is "TGAC").
Given: A DNA string s of length at most 1000 bp.
Return: The reverse complement sc of s.
Here's my code, which solves the problem:
Is there a nicer way to write
Is
Also, is there a way to avoid using
This is about the 3rd problem found on Rosalind, that goes like this:
In DNA strings, symbols 'A' and 'T' are complements of each other, as
are 'C' and 'G'.
The reverse complement of a DNA string s is the string sc formed by
reversing the symbols of s, then taking the complement of each symbol
(e.g., the reverse complement of "GTCA" is "TGAC").
Given: A DNA string s of length at most 1000 bp.
Return: The reverse complement sc of s.
Here's my code, which solves the problem:
(define (complement-of char)
(cond
((eq? char #\A)
#\T)
((eq? char #\T)
#\A)
((eq? char #\C)
#\G)
((eq? char #\G)
#\C)
(else
char)))
(define (apply-complements dna-string)
(reverse-list->string
(map complement-of
(string->list dna-string))))
(define (solve-problem infile-name outfile-name)
(let ((outfile-port (open-output-file outfile-name))
(infile-port (open-input-file infile-name)))
(begin
(write-line
(apply-complements (read-line infile-port)) outfile-port)
(close-output-port outfile-port))))
Is there a nicer way to write
complement-of? Perhaps something like a dictionary, or would that be too much work in this particular case?Is
solve-problem there appropriate from a style POV, or is this more appropriate?(define (solve-problem infile-name outfile-name)
(let ((outfile-port (open-output-file outfile-name))
(infile-port (open-input-file infile-name)))
(begin
(write-line
(apply-complements
(read-line infile-port))
outfile-port)
(close-output-port outfile-port))))
Also, is there a way to avoid using
begin and still make things look readableSolution
complement-ofThe nicer way to write
complement-of is with case instead of cond:(case char
((#\A) #\T)
((#\T) #\A)
((#\C) #\G)
((#\G) #\C)
(else char))Function names generally shouldn't end in
-of. (That's a way to pronounce function call, but it's not part of the name.) complement is enough. (Although some Schemes have a predefined function by that name, and it would be nice to not collide with it.)Beware of
eq? — it's not reliable on characters. (It's the equivalent of Java's == on Character). Use eqv? instead unless you know what you're doing. (case uses eqv? internally.)apply-complementsYou can simplify
apply-complements by using one of Chicken's libraries: (use (srfi 13)) to get string-map, which is like map but saves you having to convert to a list. That plus string-reverse makes apply-complements easy....but I wouldn't call it
apply-complements, because that sounds like a reference to the apply function. It returns the reverse complement of its argument, so that should be its name: reverse-complement.If you want to make it faster, try the imperative approach: allocate a string with
make-string, loop with do and string-set!. (It's useful to know how to write imperative code, even in functional languages.)solve-problemsolve-problem has a very ordinary bug: you forgot to close the input port. :) Fortunately, there are functions that do this automatically so you don't have to remember: call-with-input-file and call-with-output-file. They're also safer, because they close the ports on nonlocal exits like exceptions.The body of a
let is an implicit begin, so the begin in solve-problem is not needed.Formatting: when a function call is split across multiple lines, the arguments are usually aligned, like this:
(write-line (apply-complements (read-line infile-port))
outfile-port)solve-problem has a vague name. How about a description of what it does, such as reverse-complement-file? (Names are the best comments.)...however,
reverse-complement is only one of many operations you might want to apply to a file this way. I'd probably write it as a general-purpose transform-file that takes the operation as a parameter: (transform-file reverse-complement src dest). Or I'd write it as two separate operations for reading and writing, since they're often useful separately. (I'd also use read-string and write-string instead of read-line and write-line, so they'd work on files of more than one line.)Code Snippets
(case char
((#\A) #\T)
((#\T) #\A)
((#\C) #\G)
((#\G) #\C)
(else char))(write-line (apply-complements (read-line infile-port))
outfile-port)Context
StackExchange Code Review Q#49369, answer score: 3
Revisions (0)
No revisions yet.