Search code examples
clojure

clojure java.lang.NullPointerException while spliting string


I am new to clojure. I am trying to write a program which reads data from a file (comma seperated file) after reading the data I am trying to split each line while delimiter "," but I am facing the below error:

CompilerException java.lang.NullPointerException, compiling:(com\clojure\apps\StudentRanks.clj:26:5)

Here is my code:

(ns com.clojure.apps.StudentRanks)
(require '[clojure.string :as str])

 (defn student []
  (def dataset (atom []))
  (def myList (atom ()))
  (def studObj (atom ()))
   (with-open [rdr (clojure.java.io/reader "e:\\example.txt")]
    (swap! dataset into (reduce conj [] (line-seq rdr)))
   )
  (println @dataset)
  (def studentCount (count @dataset))
  (def ind (atom 0))
  (loop [n studentCount]
  (when (>= n 0)
   (swap! myList conj (get @dataset n))
   (println (get @dataset n))
   (recur (dec n))))
  (println myList)
  (def scount (count @dataset))
  (loop [m scount]
 (when (>= m 0)
   (def data(get @dataset m))
   (println (str/split data #","))
   (recur (dec m))))
  )
(student)

Thanks in advance.


Solution

  • As pointed out in the comments, the first problem is that you are not writing correct Clojure. To start, def should never be nested -- it's not going to behave like you hope. Use let to introduce local variables (usually just called locals because it's weird to call variables things that don't vary). Second, block-like constructs (such as do, let or with-open evaluates to the value of their last expression.

    So this snippet

    (def dataset (atom []))
    (with-open [rdr (clojure.java.io/reader "e:\\example.txt")]
      (swap! dataset into (reduce conj [] (line-seq rdr))))
    

    should be written

    (let [dataset
          (with-open [rdr (clojure.java.io/reader "e:\\example.txt")]
            (into [] (line-seq rdr)))]
      ; code using dataset goes here
    )
    

    Then you try to convert dataset (a vector) to a list (myList) by traversing it backwards and consing on the list under construction. It's not needed. You can get a sequence (list-like) out of a vector by just calling seq on it. (Or rseq if you want the list to be reversed.)

    Last, you iterate once again to split and print each item held in dataset. Explicit iteration with indices is pretty unusual in Clojure, prefer reduce, doseq, into etc.

    Here are two ways to write student:

    (defn student [] ; just for print
      (with-open [rdr (clojure.java.io/reader "e:\\example.txt")]
        (doseq [data (line-seq rdr)]
          (println (str/split data #",")))))
    
    (defn student [] ; to return a value
      (with-open [rdr (clojure.java.io/reader "e:\\example.txt")]
        (into []
          (for [data (line-seq rdr)]
            (str/split data #",")))))
    

    I hope this will help you to better get Clojure.