Re: Improving a nested if, or How to use multimethods the right way.
I'd just use a cond to flatten a nested if. That's usually all you need, imo. On Thu, Sep 5, 2013 at 1:07 PM, Bruno Kim Medeiros Cesar brunokim...@gmail.com wrote: Thanks for your suggestion, didn't know about that! One of the things that made someone say that Clojure looks like a language from the near future. However, I'm having a hard time using it with its full power. Could you recommend any other resource, besides the overview page on github, to learn pattern matching? Maybe a project that uses them? For the record, my code uses a simple truth table now: (defn add-edge ([g v1 v2 vs] (add-edge g (concat [v1 v2] vs))) ([g edge] (let [two? (= 2 (count edge)) dist? (apply distinct? edge) e (match [(hyper? g) (looped? g)] ; e will be nil if edge is invalid for this graph [false false] (when (and two? dist?) edge) [false true ] (when two? edge) [true false] (when dist? edge) [true true ] edge] (if e (update-in g [:edges] conj (if (directed? g) (vec e) (set e))) g On Wednesday, September 4, 2013 7:07:06 PM UTC-3, Leonardo Borges wrote: You could use pattern matching with core.match On 05/09/2013 6:57 AM, Bruno Kim Medeiros Cesar bruno...@gmail.com wrote: I'm writing (another) basic graph library, and would like to treat inputs depending on the type of the graph. A graph can be - Directed, in which case edges are vectors. Otherwise, edges are sets; - Looped, allowing edges from a node to itself; - Pseudo (or multi), allowing multiples edges between the same endpoints; and - Hyper, allowing edges with more than two vertices. To illustrate better these characteristics you can think of a scientific publication network as a directed, looped, pseudo-hypergraph. Vertices are authors, and edges are articles containing multiple researchers (hyper) who can publish alone (looped). There are multiple articles between the same researchers (pseudo) and in some contexts author order matters (directed). Now, I've created a flowchart http://imgur.com/IdgsGFG to decide if an edge should be conjed in a graph :edges entry, that leads to the following straightforward function: (defn add-edge ([graph v1 v2 vs] (add-edge graph (concat [v1 v2] vs))) ([graph edge] (if (and (multi? graph) (not= 2 (count edge))) graph (if (and (looped? graph) (not (distinct? edge))) graph (let [e (if (directed? edge) (vec edge) (set edge))] (update-in graph [:edges] conj e)) That looks ugly and a pattern that could propagate in a codebase. So I tried to factor out multimethods from it, and ended with the following: (defmulti ^:private add-edge0 (fn [g e] (hyper? g))) (defmulti ^:private add-edge1 (fn [g e] (looped? g))) (defmulti ^:private add-edge2 (fn [g e] (directed? g))) (defn ^:private add-edge3 [g e] (update-in g [:edges] conj e)) (defmethod add-edge0 :hyper [g e] (add-edge1 g e)) (defmethod add-edge0 :default [g e] (if (= 2 (count e)) **(add-edge1 g e) **g)) (defmethod add-edge1 :looped [g e] (add-edge2 g e)) (defmethod add-edge1 :default [g e] (if (distinct? e) **(add-edge2 g e) ** g)) (defmethod add-edge2 :directed [g e] (add-edge3 g (vec e))) (defmethod add-edge2 :default [g e] (add-edge3 g (set e))) (defn add-edge ([g v1 v2 vs] (add-edge g (concat [v1 v2] vs))) ([g edge] (add-edge0 g edge))) That doesn't look much better, as the amount of boilerplate increased, but at least the concerns for each type are separated. Do you have any suggestions on how to improve this design? Thanks for any consideration! Bruno Kim Medeiros Cesar Engenheiro de Computação Pesquisador em Redes Complexas www.brunokim.com.br -- -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clo...@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+u...@**googlegroups.com For more options, visit this group at http://groups.google.com/**group/clojure?hl=enhttp://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@**googlegroups.com. For more options, visit https://groups.google.com/**groups/opt_outhttps://groups.google.com/groups/opt_out . -- -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your
Re: Improving a nested if, or How to use multimethods the right way.
;; Better yet... (if (or (and (multi? graph) (not= 2 (count edge))) (and (looped? graph) (not (distinct? edge graph (let [e (if (directed? edge) (vec edge) (set edge))] (update-in graph [:edges] conj e On Thu, Sep 5, 2013 at 2:22 PM, Alex Baranosky alexander.barano...@gmail.com wrote: I'd just use a cond to flatten a nested if. That's usually all you need, imo. On Thu, Sep 5, 2013 at 1:07 PM, Bruno Kim Medeiros Cesar brunokim...@gmail.com wrote: Thanks for your suggestion, didn't know about that! One of the things that made someone say that Clojure looks like a language from the near future. However, I'm having a hard time using it with its full power. Could you recommend any other resource, besides the overview page on github, to learn pattern matching? Maybe a project that uses them? For the record, my code uses a simple truth table now: (defn add-edge ([g v1 v2 vs] (add-edge g (concat [v1 v2] vs))) ([g edge] (let [two? (= 2 (count edge)) dist? (apply distinct? edge) e (match [(hyper? g) (looped? g)] ; e will be nil if edge is invalid for this graph [false false] (when (and two? dist?) edge) [false true ] (when two? edge) [true false] (when dist? edge) [true true ] edge] (if e (update-in g [:edges] conj (if (directed? g) (vec e) (set e))) g On Wednesday, September 4, 2013 7:07:06 PM UTC-3, Leonardo Borges wrote: You could use pattern matching with core.match On 05/09/2013 6:57 AM, Bruno Kim Medeiros Cesar bruno...@gmail.com wrote: I'm writing (another) basic graph library, and would like to treat inputs depending on the type of the graph. A graph can be - Directed, in which case edges are vectors. Otherwise, edges are sets; - Looped, allowing edges from a node to itself; - Pseudo (or multi), allowing multiples edges between the same endpoints; and - Hyper, allowing edges with more than two vertices. To illustrate better these characteristics you can think of a scientific publication network as a directed, looped, pseudo-hypergraph. Vertices are authors, and edges are articles containing multiple researchers (hyper) who can publish alone (looped). There are multiple articles between the same researchers (pseudo) and in some contexts author order matters (directed). Now, I've created a flowchart http://imgur.com/IdgsGFG to decide if an edge should be conjed in a graph :edges entry, that leads to the following straightforward function: (defn add-edge ([graph v1 v2 vs] (add-edge graph (concat [v1 v2] vs))) ([graph edge] (if (and (multi? graph) (not= 2 (count edge))) graph (if (and (looped? graph) (not (distinct? edge))) graph (let [e (if (directed? edge) (vec edge) (set edge))] (update-in graph [:edges] conj e)) That looks ugly and a pattern that could propagate in a codebase. So I tried to factor out multimethods from it, and ended with the following: (defmulti ^:private add-edge0 (fn [g e] (hyper? g))) (defmulti ^:private add-edge1 (fn [g e] (looped? g))) (defmulti ^:private add-edge2 (fn [g e] (directed? g))) (defn ^:private add-edge3 [g e] (update-in g [:edges] conj e)) (defmethod add-edge0 :hyper [g e] (add-edge1 g e)) (defmethod add-edge0 :default [g e] (if (= 2 (count e)) **(add-edge1 g e) **g)) (defmethod add-edge1 :looped [g e] (add-edge2 g e)) (defmethod add-edge1 :default [g e] (if (distinct? e) **(add-edge2 g e) ** g)) (defmethod add-edge2 :directed [g e] (add-edge3 g (vec e))) (defmethod add-edge2 :default [g e] (add-edge3 g (set e))) (defn add-edge ([g v1 v2 vs] (add-edge g (concat [v1 v2] vs))) ([g edge] (add-edge0 g edge))) That doesn't look much better, as the amount of boilerplate increased, but at least the concerns for each type are separated. Do you have any suggestions on how to improve this design? Thanks for any consideration! Bruno Kim Medeiros Cesar Engenheiro de Computação Pesquisador em Redes Complexas www.brunokim.com.br -- -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clo...@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+u...@**googlegroups.com For more options, visit this group at http://groups.google.com/**group/clojure?hl=enhttp://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@**googlegroups.com. For more options, visit
Re: Improving a nested if, or How to use multimethods the right way.
Thanks for your suggestion, didn't know about that! One of the things that made someone say that Clojure looks like a language from the near future. However, I'm having a hard time using it with its full power. Could you recommend any other resource, besides the overview page on github, to learn pattern matching? Maybe a project that uses them? For the record, my code uses a simple truth table now: (defn add-edge ([g v1 v2 vs] (add-edge g (concat [v1 v2] vs))) ([g edge] (let [two? (= 2 (count edge)) dist? (apply distinct? edge) e (match [(hyper? g) (looped? g)] ; e will be nil if edge is invalid for this graph [false false] (when (and two? dist?) edge) [false true ] (when two? edge) [true false] (when dist? edge) [true true ] edge] (if e (update-in g [:edges] conj (if (directed? g) (vec e) (set e))) g On Wednesday, September 4, 2013 7:07:06 PM UTC-3, Leonardo Borges wrote: You could use pattern matching with core.match On 05/09/2013 6:57 AM, Bruno Kim Medeiros Cesar bruno...@gmail.comjavascript: wrote: I'm writing (another) basic graph library, and would like to treat inputs depending on the type of the graph. A graph can be - Directed, in which case edges are vectors. Otherwise, edges are sets; - Looped, allowing edges from a node to itself; - Pseudo (or multi), allowing multiples edges between the same endpoints; and - Hyper, allowing edges with more than two vertices. To illustrate better these characteristics you can think of a scientific publication network as a directed, looped, pseudo-hypergraph. Vertices are authors, and edges are articles containing multiple researchers (hyper) who can publish alone (looped). There are multiple articles between the same researchers (pseudo) and in some contexts author order matters (directed). Now, I've created a flowchart http://imgur.com/IdgsGFG to decide if an edge should be conjed in a graph :edges entry, that leads to the following straightforward function: (defn add-edge ([graph v1 v2 vs] (add-edge graph (concat [v1 v2] vs))) ([graph edge] (if (and (multi? graph) (not= 2 (count edge))) graph (if (and (looped? graph) (not (distinct? edge))) graph (let [e (if (directed? edge) (vec edge) (set edge))] (update-in graph [:edges] conj e)) That looks ugly and a pattern that could propagate in a codebase. So I tried to factor out multimethods from it, and ended with the following: (defmulti ^:private add-edge0 (fn [g e] (hyper? g))) (defmulti ^:private add-edge1 (fn [g e] (looped? g))) (defmulti ^:private add-edge2 (fn [g e] (directed? g))) (defn ^:private add-edge3 [g e] (update-in g [:edges] conj e)) (defmethod add-edge0 :hyper [g e] (add-edge1 g e)) (defmethod add-edge0 :default [g e] (if (= 2 (count e)) (add-edge1 g e) g)) (defmethod add-edge1 :looped [g e] (add-edge2 g e)) (defmethod add-edge1 :default [g e] (if (distinct? e) (add-edge2 g e) g)) (defmethod add-edge2 :directed [g e] (add-edge3 g (vec e))) (defmethod add-edge2 :default [g e] (add-edge3 g (set e))) (defn add-edge ([g v1 v2 vs] (add-edge g (concat [v1 v2] vs))) ([g edge] (add-edge0 g edge))) That doesn't look much better, as the amount of boilerplate increased, but at least the concerns for each type are separated. Do you have any suggestions on how to improve this design? Thanks for any consideration! Bruno Kim Medeiros Cesar Engenheiro de Computação Pesquisador em Redes Complexas www.brunokim.com.br -- -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clo...@googlegroups.comjavascript: Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+u...@googlegroups.com javascript: For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com javascript:. For more options, visit https://groups.google.com/groups/opt_out. -- -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because
Re: Improving a nested if, or How to use multimethods the right way.
Thanks, Alex, I was going down an over-engineering rabbit hole. Your solution just lacks a not before multi? and looped?. On Thursday, September 5, 2013 6:24:13 PM UTC-3, Alex Baranosky wrote: ;; Better yet... (if (or (and (multi? graph) (not= 2 (count edge))) (and (looped? graph) (not (distinct? edge graph (let [e (if (directed? edge) (vec edge) (set edge))] (update-in graph [:edges] conj e On Thu, Sep 5, 2013 at 2:22 PM, Alex Baranosky alexander...@gmail.comjavascript: wrote: I'd just use a cond to flatten a nested if. That's usually all you need, imo. On Thu, Sep 5, 2013 at 1:07 PM, Bruno Kim Medeiros Cesar bruno...@gmail.com javascript: wrote: Thanks for your suggestion, didn't know about that! One of the things that made someone say that Clojure looks like a language from the near future. However, I'm having a hard time using it with its full power. Could you recommend any other resource, besides the overview page on github, to learn pattern matching? Maybe a project that uses them? For the record, my code uses a simple truth table now: (defn add-edge ([g v1 v2 vs] (add-edge g (concat [v1 v2] vs))) ([g edge] (let [two? (= 2 (count edge)) dist? (apply distinct? edge) e (match [(hyper? g) (looped? g)] ; e will be nil if edge is invalid for this graph [false false] (when (and two? dist?) edge) [false true ] (when two? edge) [true false] (when dist? edge) [true true ] edge] (if e (update-in g [:edges] conj (if (directed? g) (vec e) (set e))) g On Wednesday, September 4, 2013 7:07:06 PM UTC-3, Leonardo Borges wrote: You could use pattern matching with core.match On 05/09/2013 6:57 AM, Bruno Kim Medeiros Cesar bruno...@gmail.com wrote: I'm writing (another) basic graph library, and would like to treat inputs depending on the type of the graph. A graph can be - Directed, in which case edges are vectors. Otherwise, edges are sets; - Looped, allowing edges from a node to itself; - Pseudo (or multi), allowing multiples edges between the same endpoints; and - Hyper, allowing edges with more than two vertices. To illustrate better these characteristics you can think of a scientific publication network as a directed, looped, pseudo-hypergraph. Vertices are authors, and edges are articles containing multiple researchers (hyper) who can publish alone (looped). There are multiple articles between the same researchers (pseudo) and in some contexts author order matters (directed). Now, I've created a flowchart http://imgur.com/IdgsGFG to decide if an edge should be conjed in a graph :edges entry, that leads to the following straightforward function: (defn add-edge ([graph v1 v2 vs] (add-edge graph (concat [v1 v2] vs))) ([graph edge] (if (and (multi? graph) (not= 2 (count edge))) graph (if (and (looped? graph) (not (distinct? edge))) graph (let [e (if (directed? edge) (vec edge) (set edge))] (update-in graph [:edges] conj e)) That looks ugly and a pattern that could propagate in a codebase. So I tried to factor out multimethods from it, and ended with the following: (defmulti ^:private add-edge0 (fn [g e] (hyper? g))) (defmulti ^:private add-edge1 (fn [g e] (looped? g))) (defmulti ^:private add-edge2 (fn [g e] (directed? g))) (defn ^:private add-edge3 [g e] (update-in g [:edges] conj e)) (defmethod add-edge0 :hyper [g e] (add-edge1 g e)) (defmethod add-edge0 :default [g e] (if (= 2 (count e)) **(add-edge1 g e) **g)) (defmethod add-edge1 :looped [g e] (add-edge2 g e)) (defmethod add-edge1 :default [g e] (if (distinct? e) **(add-edge2 g e) ** g)) (defmethod add-edge2 :directed [g e] (add-edge3 g (vec e))) (defmethod add-edge2 :default [g e] (add-edge3 g (set e))) (defn add-edge ([g v1 v2 vs] (add-edge g (concat [v1 v2] vs))) ([g edge] (add-edge0 g edge))) That doesn't look much better, as the amount of boilerplate increased, but at least the concerns for each type are separated. Do you have any suggestions on how to improve this design? Thanks for any consideration! Bruno Kim Medeiros Cesar Engenheiro de Computação Pesquisador em Redes Complexas www.brunokim.com.br -- -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clo...@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+u...@**googlegroups.com For more options, visit this group at
Improving a nested if, or How to use multimethods the right way.
I'm writing (another) basic graph library, and would like to treat inputs depending on the type of the graph. A graph can be - Directed, in which case edges are vectors. Otherwise, edges are sets; - Looped, allowing edges from a node to itself; - Pseudo (or multi), allowing multiples edges between the same endpoints; and - Hyper, allowing edges with more than two vertices. To illustrate better these characteristics you can think of a scientific publication network as a directed, looped, pseudo-hypergraph. Vertices are authors, and edges are articles containing multiple researchers (hyper) who can publish alone (looped). There are multiple articles between the same researchers (pseudo) and in some contexts author order matters (directed). Now, I've created a flowchart http://imgur.com/IdgsGFG to decide if an edge should be conjed in a graph :edges entry, that leads to the following straightforward function: (defn add-edge ([graph v1 v2 vs] (add-edge graph (concat [v1 v2] vs))) ([graph edge] (if (and (multi? graph) (not= 2 (count edge))) graph (if (and (looped? graph) (not (distinct? edge))) graph (let [e (if (directed? edge) (vec edge) (set edge))] (update-in graph [:edges] conj e)) That looks ugly and a pattern that could propagate in a codebase. So I tried to factor out multimethods from it, and ended with the following: (defmulti ^:private add-edge0 (fn [g e] (hyper? g))) (defmulti ^:private add-edge1 (fn [g e] (looped? g))) (defmulti ^:private add-edge2 (fn [g e] (directed? g))) (defn ^:private add-edge3 [g e] (update-in g [:edges] conj e)) (defmethod add-edge0 :hyper [g e] (add-edge1 g e)) (defmethod add-edge0 :default [g e] (if (= 2 (count e)) (add-edge1 g e) g)) (defmethod add-edge1 :looped [g e] (add-edge2 g e)) (defmethod add-edge1 :default [g e] (if (distinct? e) (add-edge2 g e) g)) (defmethod add-edge2 :directed [g e] (add-edge3 g (vec e))) (defmethod add-edge2 :default [g e] (add-edge3 g (set e))) (defn add-edge ([g v1 v2 vs] (add-edge g (concat [v1 v2] vs))) ([g edge] (add-edge0 g edge))) That doesn't look much better, as the amount of boilerplate increased, but at least the concerns for each type are separated. Do you have any suggestions on how to improve this design? Thanks for any consideration! Bruno Kim Medeiros Cesar Engenheiro de Computação Pesquisador em Redes Complexas www.brunokim.com.br -- -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Re: Improving a nested if, or How to use multimethods the right way.
You could use pattern matching with core.match On 05/09/2013 6:57 AM, Bruno Kim Medeiros Cesar brunokim...@gmail.com wrote: I'm writing (another) basic graph library, and would like to treat inputs depending on the type of the graph. A graph can be - Directed, in which case edges are vectors. Otherwise, edges are sets; - Looped, allowing edges from a node to itself; - Pseudo (or multi), allowing multiples edges between the same endpoints; and - Hyper, allowing edges with more than two vertices. To illustrate better these characteristics you can think of a scientific publication network as a directed, looped, pseudo-hypergraph. Vertices are authors, and edges are articles containing multiple researchers (hyper) who can publish alone (looped). There are multiple articles between the same researchers (pseudo) and in some contexts author order matters (directed). Now, I've created a flowchart http://imgur.com/IdgsGFG to decide if an edge should be conjed in a graph :edges entry, that leads to the following straightforward function: (defn add-edge ([graph v1 v2 vs] (add-edge graph (concat [v1 v2] vs))) ([graph edge] (if (and (multi? graph) (not= 2 (count edge))) graph (if (and (looped? graph) (not (distinct? edge))) graph (let [e (if (directed? edge) (vec edge) (set edge))] (update-in graph [:edges] conj e)) That looks ugly and a pattern that could propagate in a codebase. So I tried to factor out multimethods from it, and ended with the following: (defmulti ^:private add-edge0 (fn [g e] (hyper? g))) (defmulti ^:private add-edge1 (fn [g e] (looped? g))) (defmulti ^:private add-edge2 (fn [g e] (directed? g))) (defn ^:private add-edge3 [g e] (update-in g [:edges] conj e)) (defmethod add-edge0 :hyper [g e] (add-edge1 g e)) (defmethod add-edge0 :default [g e] (if (= 2 (count e)) (add-edge1 g e) g)) (defmethod add-edge1 :looped [g e] (add-edge2 g e)) (defmethod add-edge1 :default [g e] (if (distinct? e) (add-edge2 g e) g)) (defmethod add-edge2 :directed [g e] (add-edge3 g (vec e))) (defmethod add-edge2 :default [g e] (add-edge3 g (set e))) (defn add-edge ([g v1 v2 vs] (add-edge g (concat [v1 v2] vs))) ([g edge] (add-edge0 g edge))) That doesn't look much better, as the amount of boilerplate increased, but at least the concerns for each type are separated. Do you have any suggestions on how to improve this design? Thanks for any consideration! Bruno Kim Medeiros Cesar Engenheiro de Computação Pesquisador em Redes Complexas www.brunokim.com.br -- -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out. -- -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.