more idiomatic clojure

65 views
Skip to first unread message

Sunil S Nandihalli

unread,
Dec 3, 2010, 4:42:44 AM12/3/10
to clo...@googlegroups.com

Hello everybody,
I would like to know as to how I would write the following code in a more idiomatic way.

(let [mp (atom {})
      d [#{#{1 2}
           #{3 4}} ;node1
         #{#{5 6}
           #{7 8}} ;node2]]
  (dorun (for [nd d nd-pair nd face nd-pair]
           (swap! mp update-in [face] #(conj % nd))))
  @mp)

; if you consider that points 1 2 3 4 form the node node1 and 5 6 7 8 form node2
;I would like a map in the opposite direction ..
;i.e. I should be able to find out all the nodes of which 1 is part of and
;that is what the above code is doing.. but would like to know as to what
;would be a more idiomatic way of doing this.


I don't like the fact that I am using atom and swap .. can anybody suggest a better way to do it?
Thanks, Sunil.

Laurent PETIT

unread,
Dec 3, 2010, 5:18:21 AM12/3/10
to clo...@googlegroups.com
Hello,

one way:

(apply
  merge-with
  conj
  {}
  (for [nd d nd-pair nd face nd-pair]
    {face nd}))

The idea is (as is generally the case for me when trying to move from imperative code to functional), is to try not to do too many things in a single step.
So here, first I extract from the "for" "pieces of information", individual pairs of face => containing-node into an intermediate sequence.
Then I consume this sequence with merge-with.
I could also consume this sequence with reduce, of course:
(reduce (fn [m fnd] (merge-with conj m fnd) (for ...))


HTH,

--
Laurent


2010/12/3 Sunil S Nandihalli <sunil.na...@gmail.com>
--
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=en

Sunil S Nandihalli

unread,
Dec 3, 2010, 5:23:19 AM12/3/10
to clo...@googlegroups.com
Thanks Laurent.. That really helps and also thanks for the tip of how to try to do it in general.
Sunil.

Rayne

unread,
Dec 3, 2010, 5:32:10 AM12/3/10
to Clojure
Here is what I came up with.

(let [d [#{#{1 2} #{3 4}} #{#{5 6} #{7 8}}]]
(reduce
(fn [mp nd]
(apply merge
(for [nd-pair nd face nd-pair] (update-in mp [face] #(conj
% nd))))) {} d))

On Dec 3, 3:42 am, Sunil S Nandihalli <sunil.nandiha...@gmail.com>
wrote:

Andreas Kostler

unread,
Dec 3, 2010, 5:51:50 AM12/3/10
to clo...@googlegroups.com
Hi All,
May I cite an Author of a populer Clojure book:

"If you find yourself wishing yourself to repeatedly check a work queue to see if there's an item of work to be popped off,
or if you want to use a queue to send a task to another thread, you do *not* want the PersistenQueue discussed in this section"

Why do I not want to use clojure.lang.PersistentQueue for that purpose and what would I want to use instead?
Can anyone fill me in please?

Rasmus Svensson

unread,
Dec 3, 2010, 7:28:23 AM12/3/10
to clo...@googlegroups.com
2010/12/3 Andreas Kostler <andreas.koe...@gmail.com>:

clojure.lang.PersistentQueue is a collection data structure with queue
operations. It is persistent, so it lets you (and other threads)
create modified versions of it with elements enqueued or dequeued in a
thread-safe manner.

java.util.concurrent.LinkedBlockingQueue[1] is not a persistent data
structure. It is often used as a communication channel between
threads, since it allows its operations to block (potentially with a
timeout). One thread can dequeue (and block if the queue is empty)
simultaneously as another thread can enqueue (and block if the queue
is full) at the other end. It makes it easy to make programs in a
producer-consumer style.

// raek

[1] http://download.oracle.com/javase/6/docs/api/java/util/concurrent/LinkedBlockingQueue.html

Sunil S Nandihalli

unread,
Dec 3, 2010, 11:01:14 AM12/3/10
to clo...@googlegroups.com
thanks Rayne.

Ken Wesson

unread,
Dec 3, 2010, 1:40:18 PM12/3/10
to clo...@googlegroups.com
As a general rule, if you have something that has the semantics of a
pure function, and you can see how to implement it by banging on an
atom with swap! in a loop that consumes a sequence, then you can
transform it to use reduce.

If you have:

(defn foo [coll]
(let [x (atom bar)]
(dorun [y coll]
(swap! x f y))
@x))

you can change it to:

(defn foo [coll]
(reduce f bar coll))

:)

George Jahad

unread,
Dec 3, 2010, 3:39:32 PM12/3/10
to Clojure

> (apply
>   merge-with
>   conj
>   {}
>   (for [nd d nd-pair nd face nd-pair]
>     {face nd}))

I like to use into for cases like this:

(into {} (for [nd d nd-pair nd face nd-pair] [face nd]))


seems clearer to me.

g

Laurent PETIT

unread,
Dec 3, 2010, 3:46:43 PM12/3/10
to clo...@googlegroups.com
Yes,

though I've always found 'into a little bit too magical for me.
For example, I find it hard to follow the doc to see what 'adding' will mean for maps.

2010/12/3 George Jahad <clo...@blackbirdsystems.net>

g

George Jahad

unread,
Dec 3, 2010, 5:00:30 PM12/3/10
to Clojure
Actually my solution is wrong! It works for this particular example,
but not if there are nodes with overlapping values.
Doh!

My main point was just that "into" is a under used gem, that I wanted
to publicize a bit. Next time I'll try to find an example that is
actually correct!


On Dec 3, 12:46 pm, Laurent PETIT <laurent.pe...@gmail.com> wrote:
> Yes,
>
> though I've always found 'into a little bit too magical for me.
> For example, I find it hard to follow the doc to see what 'adding' will mean
> for maps.
>
> 2010/12/3 George Jahad <cloj...@blackbirdsystems.net>
>
>
>
> > > (apply
> > >   merge-with
> > >   conj
> > >   {}
> > >   (for [nd d nd-pair nd face nd-pair]
> > >     {face nd}))
>
> > I like to use into for cases like this:
>
> >  (into {} (for [nd d nd-pair nd face nd-pair]  [face nd]))
>
> > seems clearer to me.
>
> > g
>
> > --
> > 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<clojure%2Bunsu...@googlegroups.com>

George Jahad

unread,
Dec 3, 2010, 5:35:10 PM12/3/10
to Clojure
hmmm, seems like you, me and rayne all fail to handle the overlapping
node case correctly.
this is the best solution i've come up with that actually seems to
work:

(apply
merge-with
concat
{}
(for [nd d nd-pair nd face nd-pair]
{face (list nd)}))

try it with d set to this:

[d [#{#{1 2} #{3 5}}
#{#{1 2} #{3 4}}
#{#{5 6} #{7 8}}]]
Reply all
Reply to author
Forward
0 new messages