What is the difference and issues between these two clojure functions?

KDecker

For part of a class project I am implementing a function to read some data from a file and create a graph structure based on the file. Throughout the day I have asked a few questions and it has come down to this.

Below is a function that works as it should. It first reads in a file as a lazy sequence and then loops over the sequence parsing each line and printing it out.

(defn printGraph [filename, numnodes]
  (with-open [rdr (io/reader filename)]
    (let [lines (line-seq rdr)]
      (loop [curline (first lines)
             restlines (rest lines)]
        (println (lineToEdge curline))
        (cond (= 0 (count restlines)) curline
              :else
              (recur (first restlines)
                     (rest restlines)))))))

Here I use a function lineToEdge to parse a line in the file to an edge in a graph, the function is below

(defn lineToEdge [line]
  (cond (.startsWith line "e")
        (let [split-line (into [] (.split line " "))
              first-str (get split-line 1)
              second-str (get split-line 2)]
          [(dec (read-string first-str)) (dec (read-string second-str))])))

Using this function and others provided by the assignment I can tell that it works to parse the line into the proper format to add it to a graph

finalproject.core> (add-edge (empty-graph 10) (lineToEdge "e 2 10"))
[#{} #{9} #{} #{} #{} #{} #{} #{} #{} #{1}]

So from this I can tell that given a parsed line from lineToEdge I can add it to a graph as it is represented by the program.

Now my issue starts when I want to add the edges to the graph from the file. It seems when I add in the logic to the function to add the lines to the graph I get an error I just cannot track down or determine its cause. The function with this logic is seen below

(defn readGraph [filename, numnodes]
  (with-open [rdr (io/reader filename)]
    (let [lines (line-seq rdr)]
      (loop [graph (empty-graph numnodes)
             curline (first lines)
             restlines (rest lines)]
        (add-edge graph (lineToEdge curline))
        (cond (= 0 (count restlines)) graph
              :else
              (recur (graph)
                     (first restlines)
                     (rest restlines)))))))

Even apart from trying to add the edges to the graph if I simply allow graph (empty-graph numnodes) in the loop and recur with (graph) never changing it I still get the same error, which is given below

finalproject.core> (readGraphEdges "/home/eccomp/finalproject/resources/11nodes.txt" 11)
ArityException Wrong number of args (0) passed to: PersistentVector  clojure.lang.AFn.throwArity (AFn.java:429)

From here I am not sure where the error lies, I mean I can read the error and interpret it but it leads me now where. The Clojure stack trace leaves no clues for me either.

Can anyone identify where the issue lies?

Magos

As Diego Basch mentions, the error message happens because you try to call your graph (a vector of sets) as a function of no arguments: (graph). And even if you remove the parens, it is still going to recur with the unchanged graph that was originally input to the loop. add-edge is returning a new, different graph which is the one you actually want to recur with:

(defn readGraph [filename, numnodes]
  (with-open [rdr (io/reader filename)]
    (let [lines (line-seq rdr)]
      (loop [graph (empty-graph numnodes)
             curline (first lines)
             restlines (rest lines)]
        (cond (= 0 (count restlines)) graph
              :else
              (recur (add-edge graph (lineToEdge curline))
                     (first restlines)
                     (rest restlines)))))))

but this too has an issue: In the case where there are no more lines to read we don't actually call add-edge on the graph, so we leave out one edge. This seems like an easy fix: just do that before we return:

(defn readGraph [filename, numnodes]
  (with-open [rdr (io/reader filename)]
    (let [lines (line-seq rdr)]
      (loop [graph (empty-graph numnodes)
             curline (first lines)
             restlines (rest lines)]
        (cond (= 0 (count restlines)) (add-edge graph (lineToEdge curline))
              :else
              (recur (add-edge graph (lineToEdge curline))
                     (first restlines)
                     (rest restlines)))))))

Now this seems to work for me (I'm just building a 4-node complete graph in my test case) but if you want to really grok functional programming it can definitely be improved a bit. In particular we want to notice that the loop is ultimately doing operations that look like

1 + 2 + 3 + 4 + ... = (((((1 + 2) + 3) + 4) + ...

That is, first we make an empty graph, then we add an edge to that to make a new graph, then we add an edge to that graph and so on.
This is a pretty common type of operation in mathematics and it's often called a "left fold" (because you start at the left and "propagate" intermediate results rightward) or a "reduction". Most functional languages like to make this pattern explicit using a higher order function; in Clojure it's called reduce. Its arguments are

  • A function of two arguments. The first is a "value so far" and the second is a new value to incorporate. Your add-edge function works like this, taking a graph and an edge and making a new graph with that edge in it.
  • An optional starting value, such as our initial empty graph.
  • A sequence of values to thread through the function.

This is a pretty powerful technique that is able to concisely (and predictably/correctly/without off-by-one errors like I did at the start) do everything you here do with loop. It can take a bit of a mental shift to start thinking in terms of patterns like this but once you do functional programming tends to make a lot more sense and you notice the patterns cropping up almost everywhere. So since this is a class assignment, I'd recommend trying to wrangle this problem into a reduce format for yourself.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

What is the difference and issues between these two clojure functions?

From Dev

What is the difference between these two functions in Javascript?

From Java

What's the difference between these two functions

From Dev

What's the difference between these two functions?

From Dev

What is the difference between these two recursive functions?

From Dev

What is the difference between these two recursive ocaml functions?

From Dev

What's the difference between these two functions in JavaScript?

From Dev

What is the difference between these two ruby functions?

From Dev

What exactly is the difference between these two javascript functions?

From Dev

What is the difference between these two recursive functions?

From Dev

What is the difference between these two ruby functions?

From Dev

What is the difference between scope of these two functions

From Dev

What are all the difference between these two Javascript functions?

From Dev

What is the difference between [] and '[] in Clojure

From Dev

What is the difference between [] and '[] in Clojure

From Dev

What is the difference between these two functions using async/await/TPL?

From Dev

What's the difference between these two ways to declare functions?

From Dev

What is the essential difference between the two functions to find the first negative entry?

From Dev

What is the difference between these two ways of changing a functions prototype in JavaScript?

From Dev

What is the essential difference between the two functions to find the first negative entry?

From Dev

What is the difference between these two C functions in terms of handling memory?

From Dev

Javascript : What is the difference between these two fat arrow functions?

From Java

What is the difference between these arrow functions

From Dev

What is the difference between functions and closures?

From Dev

What is the difference between functions and closures?

From Dev

What is difference between 2 functions

From Dev

What is the difference between mapcat in Clojure and concatmap in Haskell?

From Dev

What is the difference between import calls in Clojure?

From Dev

What's the difference between a sequence and a collection in Clojure

Related Related

  1. 1

    What is the difference and issues between these two clojure functions?

  2. 2

    What is the difference between these two functions in Javascript?

  3. 3

    What's the difference between these two functions

  4. 4

    What's the difference between these two functions?

  5. 5

    What is the difference between these two recursive functions?

  6. 6

    What is the difference between these two recursive ocaml functions?

  7. 7

    What's the difference between these two functions in JavaScript?

  8. 8

    What is the difference between these two ruby functions?

  9. 9

    What exactly is the difference between these two javascript functions?

  10. 10

    What is the difference between these two recursive functions?

  11. 11

    What is the difference between these two ruby functions?

  12. 12

    What is the difference between scope of these two functions

  13. 13

    What are all the difference between these two Javascript functions?

  14. 14

    What is the difference between [] and '[] in Clojure

  15. 15

    What is the difference between [] and '[] in Clojure

  16. 16

    What is the difference between these two functions using async/await/TPL?

  17. 17

    What's the difference between these two ways to declare functions?

  18. 18

    What is the essential difference between the two functions to find the first negative entry?

  19. 19

    What is the difference between these two ways of changing a functions prototype in JavaScript?

  20. 20

    What is the essential difference between the two functions to find the first negative entry?

  21. 21

    What is the difference between these two C functions in terms of handling memory?

  22. 22

    Javascript : What is the difference between these two fat arrow functions?

  23. 23

    What is the difference between these arrow functions

  24. 24

    What is the difference between functions and closures?

  25. 25

    What is the difference between functions and closures?

  26. 26

    What is difference between 2 functions

  27. 27

    What is the difference between mapcat in Clojure and concatmap in Haskell?

  28. 28

    What is the difference between import calls in Clojure?

  29. 29

    What's the difference between a sequence and a collection in Clojure

HotTag

Archive