Fork me on GitHub
#beginners
<
2019-03-04
>
Namgo02:03:02

Hey all, new to clojure and enjoying it so far. One question I have is, is there a quicker way to traverse a list of lists to find the x,y coordinate than nested for statements? I'm doing that now and I wonder if it's inefficient

Namgo02:03:03

Or should I just flatten the list?

dpsutton02:03:17

these kinds of questions come up a lot. I don't see for working very well here at all though, frankly. Also, if you control the data, and you need to reach into it like this its advisable to use a data structure that gives you the addressability you like. Think maps or vectors if possible.

Namgo02:03:43

I did more reading, and clojure has a library for cartesian products, which is perfect here

Namgo02:03:32

I'll just need to flatten it when I save it with a java function. And sorry, I did as much reading as I could and followed clojure for the brave and true all the way through

dpsutton02:03:55

do you need a particular element at position x,y or the whole sequence. I'm not sure what you're trying to accomplish

Namgo02:03:05

I'm trying to draw a line (change 0 to java.awt.Color.black) over an image read, and then save that back again

Namgo03:03:32

I'm bad at math, cartesian product is probably not what I want. I'm trying to set plane[1,1] = 1 as I would write it in python, but I'm still wrapping my head around how this all works

dpsutton03:03:33

what datastructure is plane?

Namgo03:03:37

If this were python, it would be a 2d array, but because it's not, I don't really know what to make it. It needs to be addressable by coordinates, and store a value, but I'm beginning to wonder if it should be {:x 0 :y 1 :value black}

Namgo03:03:32

https://codereview.stackexchange.com/questions/87325/starting-a-clojure-chess-engine helped a lot, so basically I was trying to use a list where I shouldn't be

Namgo04:03:32

Okay, yeah using a vector is what I need, clojure is really different from what I'm used to but that's one of the reasons I'm hoping to learn it

Nazral04:03:52

I have an issue with lein and a compojure project. If I run lein ring server-headless everything starts fine and all, but if I do lein repl I returns an error :cause Could not locate ring/middleware/reload__init.class, ring/middleware/reload.clj or ring/middleware/reload.cljc on classpath.

skykanin11:03:31

If I have a config map which is created using cli arguments in -main how do I expose that map to other namespaces?

Lucas Barbosa12:03:55

If you don't want to pass the config object around in every function as an argument, maybe you can store it in a var in the main namespace using def and set!

skykanin13:03:19

How would I store it in my core.clj namespace from within the -main function? Is there a way to set variables to a global namespace from anywhere?

Lucas Barbosa13:03:26

(def config {})
(defn -main
  [& args]
  (alter-var-root #'config
                  (constantly {:param1 "val1"
                               :param2 "val2"})))

Lucas Barbosa13:03:40

this will set the value of the var config to the provided hashmap and it will be available to other namespaces

mbjarland15:03:03

What would be an idiomatic way of removing duplicates from a coll of maps based on a predicate? Essentially the predicate would take two maps and return true/false based on whether the two are considered "equal"

mbjarland15:03:59

hmm, perhaps clojure.set/index need to play with this a bit...

borkdude15:03:18

@mbjarland a lot of helper libs have distinct-by which is not in clojure.core

mbjarland15:03:13

@borkdude I was hoping I was being daft and there was some cool combination of core functions to accomplish this but I'll take it : )

borkdude15:03:55

a less efficient version could be implemented with group-by, vals and them map + first or something

borkdude15:03:25

or maybe you can avoid having those duplicates in the first place 🙂

mbjarland15:03:11

no such luck I'm afraid : ) maybe something like this?

(defn distinct-by [keys rs]
  (map first (vals (group-by (apply juxt keys) rs))))
which is more or less what you were saying above...

borkdude15:03:59

I would make keys a regular function

borkdude15:03:27

then the function is more re-usable

borkdude15:03:32

but that’s up to you

borkdude15:03:10

so you need to get maps unique by a key combination?

mbjarland15:03:18

right, agree, that would make it more generic. Though this is a command line ad-hoc script which will never (hahaha...yeah...can't believe I'm saying this...that just never happens) be re-used : )....

borkdude15:03:52

what I would do instead is (distinct (map #(select-keys % [:a :b :c])) the-maps)

borkdude15:03:12

but then you’ll lose the extra keys, not sure if you are interested in those

borkdude15:03:53

you can also use a transducer version of this

mbjarland15:03:56

I am, just want the filtering, but want to keep whatever data comes in in the maps

borkdude15:03:31

that’s a bit weird though, since the extra keys are arbitrary

mbjarland15:03:58

well isn't the clojure philosophy that you grab the data you want from the maps that come in and pass whatever you don't care about along?

borkdude15:03:59

since they can be different, why would you keep the values of a random one around

mbjarland15:03:18

they are not random, just not relevant for the uniqueness filtering

mbjarland15:03:13

and maybe I was not explaining myself well either, there is a fixed set of keys and I would like to keep whatever keys come in, and I would like a way to filter out duplicates based on a sub-set of the keys. I agree that that would throw away one or more (arbitrary) duplicate items, but in this scenario I'm ok with that

borkdude15:03:02

if you know the unique combination of keys up front, you can also use that to not even add the maps to that input collection

drone15:03:01

e.g., are {:used-k1 x :used-k2 y :ignored-k1 b}and {:used-k1 x :used-k2 y :ignored-k1 a} duplicates?

mbjarland15:03:15

true. In this particular case I thought this was an interesting problem which I would probably run into again so I figured it was worth thinking a bit about. @borkdude thanks for all the above

mbjarland15:03:35

@mrevelle yes

drone15:03:21

so the ignored keys will have arbitrary values based on runtime implementation details, was the point @borkdude was making and I think you already understood

mbjarland15:03:38

@mrevelle yes, in this case not really runtime implementation details, but yes the same effect

mbjarland15:03:12

ah and @mrevelle in your example above, I would like to be able to define the set of keys used for uniqueness, i.e. [:used-k1 :used-k2] above

mbjarland15:03:24

eg (distinct-by [:username :birth-date] clojurians)

borkdude15:03:33

that would be (distinct-by (juxt :username :birth-date) ...) if you would have distinct-by take a function

mbjarland16:03:00

@borkdude yes, that works as well and is more generic

mbjarland16:03:59

anyway, I assumed there was some cool combination of core functions which I failed to grok. If there is - great, if not, the above already gave me a bunch of ways of solving this

mbjarland16:03:27

so thanks @mrevelle and @borkdude

mbjarland16:03:18

as performance is not a concern here, I guess I will go with:

(defn distinct-by [uniq-fn maps]
  (map first (vals (group-by uniq-fn maps))))
for now, usage as in @borkdude's example above

pez17:03:54

I need help to understand why this generates a StackOverflowError:

(defn drop-every [nth coll]
  (loop [selected '()
         candidates coll]
    (if (empty? candidates)
      selected
      (recur (concat selected (take (dec nth) candidates)) (drop nth candidates)))))

(comment
  (drop-every 3 [:a :b :c :d :e :f :g])
  ; => (:a :b :d :e :g)
  (drop-every 3 (range 10000))
  ; Error: StackOverflowError   clojure.lang.LazySeq.sval (LazySeq.java:40)
  )
Note, I know it is an inefficient solution. 😃 I have written a few of them today, most much more efficient, but I don’t understand why the stack blows up for this one.

boris17:03:26

https://stuartsierra.com/2015/04/26/clojure-donts-concat this seems particularly relevant to your example, and explains why this happens.

dpsutton17:03:24

substitute concat with into?

Daw-Ran Liou17:03:31

Quote from the link in @boris834's answer: > In the case of our concat chain, each LazySeq’s fn returns another LazySeq. seq has to recurse through them until it finds an actual value. If this recursion goes too deep, it overflows the stack.

Daw-Ran Liou17:03:58

My biggest take-away from the article is: > Don’t use lazy sequence operations in a non-lazy loop.

pez18:03:24

So, how does one write lazy loops?

Daw-Ran Liou18:03:37

I like to think of lazy-seq as producer function that returns infinite items. And never realize the lazy-seq functions on it's own.

pez18:03:22

Yes, that’s how I imagine them as well. My test for if I have managed to create a lazy function is if it goes faster if i (take 10) from it.

boris19:03:34

@pez

(defn drop-every [nth coll]
  (lazy-seq
    (when-let [s (seq coll)]
      (concat (take (dec nth) coll)
              (drop nth coll)))))
I looked to (source take-nth) for inspiration.

boris19:03:10

And then take it for a whirl (take 20 (drop-every 3 (range 100000000000)))

pez19:03:48

Doesn’t do the right thing though. 😃 Seems to only drop the first nth.

boris19:03:40

oops! forgot the recursive call

(defn drop-every [nth coll]
  (lazy-seq
    (when-let [s (seq coll)]
      (concat (take (dec nth) coll)
              (drop-every nth (drop nth coll))))))

pez19:03:06

Cool! Maybe this is a slight readability improvement:

(defn drop-every [nth coll]
  (lazy-seq
   (when-not (empty? coll)
     (concat (take (dec nth) coll)
             (drop-every nth (drop nth coll))))))

Lucas Barbosa17:03:32

I am getting this exception when I try to run a jdbc query against a mysql database and I have no clue why it is happening. Does anyone know?

Unhandled javax.naming.NoInitialContextException
   Need to specify class name in environment or system property, or in
   an application resource file: java.naming.factory.initial

Lucas Barbosa17:03:07

I have these in my dependency map in project.clj:

[mysql/mysql-connector-java "8.0.15"]
[org.clojure/java.jdbc "0.7.9"]

Daw-Ran Liou17:03:55

@pez I'm interested to see your other solutions. Would you share them? There's one solution on top of my head involves: mapped-index and filter

pez18:03:38

Something like this?

(defn drop-every [nth coll]
  (->> coll
       (map-indexed #(when (< 0 (rem (inc %1) nth)) %2))
       (remove nil?)))
😄

Daw-Ran Liou18:03:20

Yeah like this! I'm wondering if there's other way of solving this problem

Daw-Ran Liou18:03:31

(There must be.)

pez18:03:27

This one is less efficient:

(defn drop-every [nth coll]
  (let [nth-1 (dec nth)]
    (->> coll
         (partition-all nth)
         (map #(take nth-1 %))
         (apply concat))))

Daw-Ran Liou18:03:47

Thanks I like this one

pez19:03:47

Wrong issue: [edited: another wrong link]

Daw-Ran Liou19:03:46

Thanks for sharing! I've wanted to sign up more clojure newsletters for a while

pez19:03:30

Now I remembered about keep-indexed:

(defn drop-every [nth coll]
  (keep-indexed #(when (< 0 (rem (inc %1) nth)) %2) coll))

pez19:03:41

I saw your solution, but then it wasn’t there. I tried it though, and it works and is pretty fast.

pez19:03:04

Since you seem to share my interest for these kind of problems you might like this thread: https://clojureverse.org/t/eratosthenes-party-time-a-k-a-feedback-wanted-on-this-implementation-of-eratosthenes-sieve/3801/16

pez20:03:15

Turns out partitition-all has this behaviour built in:

(defn drop-every [nth coll]
  (apply concat (partition-all (dec nth) nth coll)))

Daw-Ran Liou20:03:22

Thanks for sharing the clojureverse discussion. It looks very cool and I think there're a lot of things that I can learn from

pez21:03:24

I certainly learnt a lot from it. 😃

hiredman18:03:49

@lvbarbosa *e should give you a complete stacktrace

hiredman18:03:49

@lvbarbosa *e should give you a complete stacktrace

Lucas Barbosa18:03:30

#error {
 :cause Need to specify class name in environment or system property, or in an application resource file: java.naming.factory.initial
 :via
 [{:type javax.naming.NoInitialContextException
   :message Need to specify class name in environment or system property, or in an application resource file: java.naming.factory.initial
   :at [javax.naming.spi.NamingManager getInitialContext NamingManager.java 685]}]
 :trace
 [[javax.naming.spi.NamingManager getInitialContext NamingManager.java 685]
  [javax.naming.InitialContext getDefaultInitCtx InitialContext.java 305]
  [javax.naming.InitialContext getURLOrDefaultInitCtx InitialContext.java 342]
  [javax.naming.InitialContext lookup InitialContext.java 409]
  [clojure.java.jdbc$get_connection invokeStatic jdbc.clj 419]
  [clojure.java.jdbc$get_connection invoke jdbc.clj 274]
  [clojure.java.jdbc$db_query_with_resultset_STAR_ invokeStatic jdbc.clj 1093]
  [clojure.java.jdbc$db_query_with_resultset_STAR_ invoke jdbc.clj 1075]
  [clojure.java.jdbc$query invokeS
tatic jdbc.clj 1164]
  [clojure.java.jdbc$query invoke jdbc.clj 1126]
  [clojure.java.jdbc$query invokeStatic jdbc.clj 1142]
  [clojure.java.jdbc$query invoke jdbc.clj 1126]
  [learn.core$eval5973 invokeStatic form-init2045875664231350038.clj 11]
  [learn.core$eval5973 invoke form-init2045875664231350038.clj 11]
  [clojure.lang.Compiler eval Compiler.java 7176]
  [clojure.lang.Compiler eval Compiler.java 7131]
  [clojure.core$eval invokeStatic core.clj 3214]
  [clojure.core$eval invoke core.clj 3210]
  [clojure.main$repl$read_eval_print__9068$fn__9071 invoke main.clj 414]
  [clojure.main$repl$read_eval_print__9068 invoke main.clj 414]
  [clojure.main$repl$fn__9077 invoke main.clj 435]
  [clojure.main$repl invokeStatic main.clj 435]
  [clojure.main$repl doInvoke main.clj 345]
  [clojure.lang.RestFn invoke RestFn.java 1523]
  [nrepl.middleware.interruptible_eval$evaluate invokeStatic interruptible_eval.clj 79]
  [nrepl.middleware.interruptible_eval$evaluate invoke interruptible_eval.clj 55]
  [nrepl.middleware.
interruptible_eval$interruptible_eval$fn__921$fn__925 invoke interruptible_eval.clj 142]
  [clojure.lang.AFn run AFn.java 22]
  [nrepl.middleware.session$session_exec$main_loop__1022$fn__1026 invoke session.clj 171]
  [nrepl.middleware.session$session_exec$main_loop__1022 invoke session.clj 170]
  [clojure.lang.AFn run AFn.java 22]
  [java.lang.Thread run Thread.java 834]]}

Lucas Barbosa18:03:12

I still don't understand the reason :(

hiredman18:03:57

clojure.java.jdbc tries to accept a wide range of different connection specifications, and whatever is in your connection specification makes it thing you are using jndi(which is this whole other thing) to inject the connection information, so it is trying to use jndi and getting that error when doing so

Lucas Barbosa18:03:41

I just got the latest mysql connector, maybe something changed?

Lucas Barbosa18:03:48

I'll try to get an older version

hiredman18:03:52

basically the map specifying your connection to mysql is "wrong" but wrong in a way that makes jdbc think you are trying to use jndi, and because you aren't using jndi you get that error

hiredman18:03:09

no, don't change versions

hiredman18:03:31

look at your connection spec map and compare it to the the docs and make sure it is correct

Lucas Barbosa18:03:39

this is my db-spec:

(def db-spec {:dbtype "mysql"
              :name "learn"
              :user "root"
              :password "root"})

Lucas Barbosa18:03:49

I'm probably missing something obvious

Lucas Barbosa18:03:45

ok, I found it

Lucas Barbosa18:03:54

:dbname instead of :name

Lucas Barbosa18:03:29

picard-facepalm thanks @U0NCTKEV8

pez18:03:16

@dpsutton: That works! But my question remains. 😃 I’ll read the article that @boris834 linked me to, and see if that explains it to me.