Spyros
Spyros

Reputation: 35

Passing filename to a method in Clojure

I have a filename at args[0] (e.g. (first args) and I want to pass it from my main method to the slurp-std-input . I tried to do the followings but I get this error:

CompilerException java.lang.RuntimeException: Unable to resolve symbol: enc in this context, compiling:(regexdna/core.clj:19:3)

The code for both methods is:

(defn -main
  [& args]
  (let [content (slurp-std-input (first args))
        original-len (count content)
        ;; I'd prefer if I could use the regexp #"(^>.*)?\n" like the
        ;; Perl benchmark does, but that only matches ^ at the beginning
        ;; of the string, not at the beginning of a line in the middle
        ;; of the string.
        content (str/replace content #"(^>.*|\n>.*)?\n" "")
        dna-seq-only-len (count content)]

    (doseq [[re num-matches] (pmap #(count-regex-occurrences % content)
                                   dna-seq-regexes)]
      (spit "./scratch/output.txt" (format "%s %d\n" re num-matches):append true))

    (let [content (reduce one-replacement content iub-codes)]
      (spit "./scratch/output.txt" (format "\n%d\n%d\n%d\n" original-len dna-seq-only-len (count content)) :append true)))
  (flush))

and the method in which I want to pass the (first args) parameter, so I can read from the String filename that is contained in (first args) is:

(defn slurp-std-input [args]
  ;; Reads the standard input using the encoding enc into a string and
  ;; returns it.
  ([] (slurp-std-input (.name (java.nio.charset.Charset/defaultCharset))))
  ([#^String enc]
     (with-open [r (new java.io.BufferedReader (clojure.java.io/reader args))]
       (let [sb (new StringBuilder)]
     (loop [c (.read r)]
       (if (neg? c)
         (str sb)
         (do
           (.append sb (char c))
           (recur (.read r)))))))))

Note that I have changed reading from in to reading from my file contained in args[o]

Upvotes: 1

Views: 424

Answers (2)

Dave Yarwood
Dave Yarwood

Reputation: 3010

RedDeckWins is on the right track, but I felt I could better help to clarify the problem in a separate answer, and also point out a couple of other things while I'm at it.

This should at least fix the error that you're getting:

(defn slurp-std-input ;; (1)
  "Reads the standard input using the encoding enc into a string and
   returns it." ;; (2)
  ([fname] 
    (slurp-std-input fname (.name (java.nio.charset.Charset/defaultCharset))))
  ([fname ^String enc] ;; (3)
     (with-open [r (new java.io.BufferedReader 
                     (clojure.java.io/reader fname :encoding enc))]
       (let [sb (new StringBuilder)]
         (loop [c (.read r)]
           (if (neg? c)
             (str sb)
             (do
               (.append sb (char c))
               (recur (.read r)))))))))

Take note of three things above:

  1. Here is where your problem lies -- you're defining a function with multiple arities, but the first line was (defn slurp-std-input [args] -- this means the function has the arity 1, so anything after [args] must be a single definition of your fixed-arity function. That isn't what you want, so get rid of [args] and you should be good to go.

  2. The convention in Clojure is to document functions via a string following the name of the function. This enables the user to obtain the documentation of your function via (doc slurp-std-input).

  3. Your type hinting syntax was slightly off. It should be ^String enc, not #^String enc.


EDIT: I just realized your code uses args, which will no longer work with the corrections I've made, since the symbol args isn't bound to anything... I think I've figured out your intention, though. I've edited my code above. Your function should have the possible arities 1 and 2, not 0 and 1. You're at least passing one argument -- the filename -- and you can optionally pass an encoding as well.


EDIT #2: Here is a revised version that relies on clojure.core/slurp, which conveniently allows for optional parameters such as encoding:

(defn slurp-std-input
  "Returns the contents of a file as a string. 
   Optionally takes an encoding as a second argument."
  ([fname] (slurp fname))
  ([fname ^String enc] (slurp fname :encoding enc)))

Note that this is literally just a wrapper for clojure.core/slurp, so the fact that you're slurping a filename that comes from standard input is actually irrelevant. If I were to write a function like this, I would call it something like slurp-plus or slurp' or something.

Upvotes: 0

RedDeckWins
RedDeckWins

Reputation: 2121

You have invalid syntax. If you are providing multiple implementations of a method with different arities, the proper syntax would be something like I've put below. Essentially, the args you had on the first line was incorrect.

(defn slurp-std-input
  ;; Reads the standard input using the encoding enc into a string and
  ;; returns it.
  ([fname]
   (with-open [r (new java.io.BufferedReader (clojure.java.io/reader fname))]
     (let [sb (new StringBuilder)]
       (loop [c (.read r)]
         (if (neg? c)
           (str sb)
           (do
             (.append sb (char c))
             (recur (.read r)))))))))

EDIT: changed code a bit. What was your intention with the #^String enc?

Upvotes: 1

Related Questions