Recursive pattern match on string


This is my first post here in the Clojure Forum.
I am currently trying to do my first steps in Clojure Programming by solving Clojure Koans.
Currently I ran into a problem while trying to complete a recursive pattern match on a string/ character list.
The main problem here seem to be type mismatches. However, the compiler output is not really helpful.
Any input or suggestions are appreciated!

    (ns rna-transcription
    (:require [clojure.string :as str]))

    (defn to-rna [dna] 
      (loop [dna dna rna[]]
        (if (empty dna)
           (str rna))
         (rest dna)
         (conj rna (    
           (str/replace (str(first dna)) "C" "G")     
           (str/replace (str(first dna)) "G" "C") 
           (str/replace (str(first dna)) "A" "U") 
           (str/replace (str(first dna)) "T" "A"))))))

There are couple of problems here:

  1. Format code properly
  2. Your if doesn’t have the else branch (check parentheses)
  3. Check precisely what do you conj to rna (check parentheses, currently it looks like: ((str/replace...) (....) ...))
  4. You intend to call str/replace 4 times in a row on the same input (remember, the last call will be returned). Possibly you want conditional replacement, check condp ((condp = (first dna) ...))
  5. And last… there is a function for that! :slight_smile: Try to find it in clojure.str namespace. One function to replace characters in given string: (str/... "CATG ...") ;; => "GUAC"

Good luck!

Ok, thank you!
I was partially able to follow your instructions.

  1. Placing the parenthesis correctly around the else clause: Yes, I also had the idea this is wrong (double checked Russ Olson’s book “Getting Clojure”)
  2. Instead of condp, I just made use of cond to achieve a simple pattern match.
  3. I still would like to stick to the idea of recursion instead of jumping to a map function.
    So here is an improved version, but there still seems to be a major pain-point:

(ns rna-transcription)

(defn to-rna [dna] 
   (loop [dna dna rna[]]
    (if (empty dna)
    (rest dna)
    (str (
      [first dna]    
       (= (first dna) \G) "C" 
       (= (first dna) \C) "G" 
       (= (first dna) \A) "U" 
       (= (first dna) \T) "A" 

A couple more comments… You are trying to call [first dna] as a function, with the results of your cond expression as arguments. That is almost certainly not what you want, and the compiler will complain, too.
Instead of repeating the (= (first dna) \...) bit, you could just do

(case (first dna)
  \G  "C"
  \C  "G"
  \A "U"
  \T  "A"

and so on.

I’m not entirely sure what you’re trying to achieve, but would something along this lines be close?

(defn dna->rna [dna]
  (let [transc {\G \C
                \C \G
                \A \U
                \T \A}]
    (apply str
           (reduce (fn[acc e]
                     (conj acc (transc e)))
                   [] dna))))

(dna->rna "GCCGTCCAAC")

Thank you for your reply!
Using a map / reduce function is of course another approach that is much more practicable. I will make use of these functions in the future. This time, I just wanted to stick to the recursive approach and finally got it to work.

I was finally able to solve it with a purely recursive approach!
Besides the wrong parenthesis around the else clause, my main problem was indeed proper handling of the string / character types.
The final fix I had to apply was a type conversion of the function input argument to string at the beginning of the loop (no idea why it was not a string to begin with)!
Now it looks like this:

(ns rna-transcription)

(defn to-rna [dna] 
 (loop [dna (str dna) rna ""]
  (if (empty? dna)
    (rest dna) 
        (str rna (cond 
              (= (first dna) \G) "C" 
              (= (first dna) \C) "G" 
              (= (first dna) \A) "U" 
              (= (first dna) \T) "A" 
              :else (throw (AssertionError. "Wrong input"))))))))