Fork me on GitHub
#clojure
<
2022-06-27
>
dvingo15:06:39

looking for some code-golf ideas: Input: vector and an item that is present in the vector output: 0-based index of that item one solution

(first (keep-indexed (fn [idx i] (when (= 'c i) idx)) '[a b c d]))

p-himik15:06:02

I'd just use loop.

dpsutton16:06:57

reduce does a lot of work to use the fastest way to go through a collection, falling back to the naive first and rest. I’d probably use reduce and reduced to take advantage of all of that

dvingo16:06:21

thanks guys!

dpsutton16:06:47

(let [needle 300000
      haystack (range 500000)
      find-seq (fn [thing coll] (first (keep-indexed (fn [idx x] (when (= thing x) idx)) coll)))
      find-reduce (fn [thing coll] (reduce (fn [idx x] (when (= thing x) (reduced idx))) coll))]
  (doseq [[style coll-fn] [[:vector vec]
                           [:array long-array]
                           [:seq seq]
                           [:lazy-seq #(filter identity %)]]]
    (println style)
    (println "find seq:")
    (time (find-seq needle (coll-fn haystack)))
    (println "find-reduced:")
    (time (find-reduce needle (coll-fn haystack)))))
:vector
find seq:
"Elapsed time: 22.897 msecs"
find-reduced:
"Elapsed time: 29.1585 msecs"
:array
find seq:
"Elapsed time: 29.998666 msecs"
find-reduced:
"Elapsed time: 13.251959 msecs"
:seq
find seq:
"Elapsed time: 8.895417 msecs"
find-reduced:
"Elapsed time: 22.358708 msecs"
:lazy-seq
find seq:
"Elapsed time: 12.185917 msecs"
find-reduced:
"Elapsed time: 7.973875 msecs"
nil

dvingo16:06:27

very cool @U11BV7MTK thanks for sharing. Here's 3 versions:

(defn find-index [c item]
  (loop [c c, i (first c), idx 0]
    (cond
      (= i item) idx
      (nil? c) nil
      :else (recur (next c) (fnext c) (inc idx)))))

(defn find-index2 [c item]
  (let [cn (count c)]
    (reduce (fn [idx i]
              (if (= i item) 
                (reduced idx)
                (when-not (= (inc idx) cn) (inc idx))))
    0 c)))

(defn find-index3 [c item]
  (get (zipmap c (range (count c))) item))
this is for low-perf code - kinda like the map elegance

p-himik16:06:26

In the loop option, just use the idx variable. You don't need c and i if c is always a vector - just use nth.

👍 1
dpsutton16:06:10

I was profiling because nth makes me quite nervous and it can be horrendously bad since it is O(n) on some collections. Use it only if you know that your collections are indexed

1
dpsutton16:06:21

(the profiling code has not returned after 30seconds 🙂 I think i’m in “heat death” territory here lol

dpsutton16:06:29

:seq
find seq:
"Elapsed time: 7.863042 msecs"
find-reduced:
"Elapsed time: 5.276917 msecs"
find-nth:
"Elapsed time: 183715.408666 msecs"

dvingo16:06:17

oh wow 🙂

p-himik16:06:29

Yeah, hence my explicit mentioning of there being "always a vector". :)

dpsutton16:06:30

for sure. just wanted to point out how catastrophic that can be if you don’t heed that valuable advice

👍 1
Ed17:06:37

You could also call .indexOf? (If it's not an array) Or is interop not allowed in code golf? No idea what the performance of that would be like on a seq ;)

p-himik17:06:21

Actually, that's probably the best answer, if it's CLJ and not CLJS.

p-himik17:06:37

And it works on CLJS too, so never mind the last part. :)

dpsutton17:06:28

where does .indexOf come from? is that java util collections method?

p-himik17:06:34

Yep, from List on CLJ and just seems to be there on CLJS.

🙏 1
dpsutton17:06:39

Yeah nice solution @U0P0TMEFJ

👍 2
reefersleep08:06:38

How come you’re optimizing for performance, @U11BV7MTK? Does “code golf” sometimes refer to how fast the code performs and not the length of the code?

reefersleep08:06:08

Or just some additional fun? 🙂

Jacob Rosenzweig17:06:08

Are there any good edn formatting tools? I have some static test edn files I need to pretty indent.

Jacob Rosenzweig18:06:27

Great CLI tool. I’ll have to use it later

zakkor17:06:12

when starting a socket REPL with clojure -J-Dclojure.server.repl='{:port 5555 :accept clojure.core.server/repl}', the commands I send to the REPL by doing something like echo '3' | nc localhost 5555 get shown in the response, on the side where I send it. Is there any way to also make the REPL itself print out what was evaluated and the evaluation result?

borkdude18:06:58

@edward.partenie Maybe prepl is what you need?

clojure -J-Dclojure.server.repl='{:port 5555 :accept clojure.core.server/io-prepl}'
$ nc localhost 5555
(+ 1 2 3)
{:tag :ret, :val "6", :ns "user", :ms 39, :form "(+ 1 2 3)"}

zakkor18:06:48

hmm, not quite, I don't think - I basically want the same output from repl, but for it to be printed inside the actual REPL instance as well

zakkor18:06:46

like making the REPL echo out any command that is sent to it...

borkdude18:06:48

(binding [*out* ( System/out)] (prn :hello))

hiredman18:06:16

Something to keep in mind is those are two different repls

hiredman18:06:31

Running in the same process, but different repls

hiredman18:06:17

So there is no "actual REPL"

hiredman18:06:23

One repl has its input and output attached to the processes stdin and stdout, the other has its input and output attached to a tcp socket

zakkor18:06:32

how can I make it do this for every command I run, though? :thinking_face:

borkdude18:06:49

@edward.partenie

user=> (require '[ :as io])
nil
user=> (set! *out* (io/writer System/out))

zakkor18:06:54

user=> (require '[ :as io])
nil
user=> (set! *out* (io/writer System/out))
Execution error (IllegalStateException) at user/eval146 (REPL:1).
Can't change/establish root binding of: *out* with set
user=> 

borkdude18:06:43

you must evaluate this in your socket repl

zakkor18:06:43

I'm not sure it totally works though, cause I'd want the result of the evaluation to be printed, not just what is outputted to stdout :thinking_face:

borkdude18:06:07

ok, well, what @U0NCTKEV8 said ;)

dpsutton18:06:31

you could start a new clojure.main repl? (clojure.main/repl :eval (fn [f] (binding [out (http://clojure.java.io/writer System/out)] (eval f))))

hiredman18:06:58

The reason I am being pedantic about the relationship between the repl's (they are peers) is because if you are building tooling, baking in faulty assumptions, like there is one "the repl" and it is using the processes stdin and stdout, at the bottom, it is going to be very annoying to dig yourself out of that hole later

zakkor18:06:06

yeah, that makes sense. I'll try to work around it a different way (by writing the result gotten from nc straight to my editor window (the one in yellow)) - thanks for the help everyone!

hiredman18:06:44

I would back up and ask about your higher level goal vs your strategy for achieving it

zakkor19:06:34

Sure - I am trying to make the editor I'm using work more nicely for REPL driven development. I've made a thing where if you highlight the selection and middle click, it does echo $selection | nc localhost 5555, and this works, but I don't get any indication of what was evaluated and the result of the evaluation is

hiredman20:06:49

In general the way editors implement that is not by starting a new repl

hiredman20:06:12

But by copying the text and sending it to the input of the existing repl

hiredman20:06:48

(there is some variation in this in editors, and some expose multiple slightly different forms of this thing)

hiredman20:06:09

a lot of existing tooling uses nrepl, which makes it somewhat easier to inject stuff in to the middle (because it is message rather than stream based) at a significant increase in complexity

zakkor20:06:09

> But by copying the text and sending it to the input of the existing repl Hmm, that gave me an idea for a much easier way of solving the whole thing. Thank you!

noisesmith16:07:49

I think you solved this already, but for the precise behavior you ask for here, you could make a custom OutputStream *out* which writes both to the existing previous *out* and the *out* of the REPL inside the editor's TTY, then use that OutputStream for the socket REPL's *out*

Braden Shepherdson19:06:29

I'm struggling to find a good way to structure an index to some data. once built, the index is immutable, which is nice. it's also a nested hierarchy, and the keys in different subtrees can collide. but that's okay I can use nested maps. the nasty bit is that there are two keys for each value, at each level of the hierarchy. so I can't naively use update-in, or the nested values will diverge. I'm tempted to make the inner maps mutable, so whichever path through the keys is taken the final value is the same. is there a way to build such a nested structure with transients, or other mutable structures, and get Clojure maps out in the end? should I use mutable Java Map types? flatten the map to a single layer where the keys are vectors of the path, using every permutation of the keys all pointing at the same value?

p-himik19:06:44

When I encounter such issues, I immediately seek ways to normalize that data.

👍 1
Drew Verlee19:06:52

what does it mean to have "two keys for each value" you mean like {"a" "drew" "b" "drew"} where changes to "a" drew and "b" drew should be synced?

Braden Shepherdson20:06:34

I think normalizing is possible. instead of a big hierarchical tree, a forest, where the upper levels of the hierarchy map to IDs rather than holding the real entities.

Drew Verlee20:06:56

You should look at datascript, it provides normalization. I'm sure there are thinner solutions.