Fork me on GitHub
#clojure
<
2020-07-13
>
wombawomba08:07:27

I’m looking to do “fuzzy substring search” à la FZF (https://github.com/junegunn/fzf), but I can’t find any Clojure/Java libraries that seem like they’d help me with this. Maybe there is one but I’ve missed it? Otherwise, any advice on how to proceed with writing my own?

Daniel Stephens09:07:17

It's probably overkill but apache lucene might help

wombawomba09:07:44

Yeah.. I’d like to keep my dependencies lightweight if possible, but I guess Lucene might be a good fallback if I can’t figure out something better 🙂

wombawomba10:07:21

Alright, turns out that implementing this thing on my own was actually pretty straightforward

wombawomba10:07:18

I’ll have to tweak various weights for the result quality to match FZF, and I might have to do some optimizations to handle large sets of strings, but overall a quick implementation of Smith-Waterman (https://en.wikipedia.org/wiki/Smith%E2%80%93Waterman_algorithm) with a few adjustments seems to work well enough

👍 3
🎉 3
Balaji Sivaramgari09:07:02

Hello All, I have 3 sub projects in my main project, like one main project.clj which calls other 3 sub project.clj 's. Now I would like to call a specific project.clj file from my project (where my main project.clj file exists). Can someone give me the command to achieve this?

noisesmith13:07:03

what do you mean by "call sub project.clj's" - is the parent project somehow consuming / transforming the child project definitions? usually you'd create an artifact from each project, and the parent project would use the artifact produced by the child

pinkfrog10:07:20

This thing has been on my mind for some time. As a Clojure newbie, I have always been thinking how to map what I learned in imperative languages to Clojure. For example, what’s the idiomatic code of doing a BFS search in Clojure? Extending that, how do we do Dijkstra and other basic algorithms in Clojure (I mean, in an idiomatic way.). We can somehow come up with some adhoc ones, but I think there shall be some pattern, some Clojure way of expressing these algorithms. I am aware of rosetta, but it is more like a place of scattered code.

p-himik10:07:16

Have you read SICP, by any chance? The older edition - the one that still used Scheme and not Python.

pinkfrog10:07:31

I re-checked the toc of sicp. It may seen a little bit abstract, and didn’t directly cover what I said. So to rephrase my question, is there a place to lookup idiomatic clojure code on, for example, topo sort ?

wombawomba11:07:57

SICP will help build your intuition around imperative vs functional code, but it won’t help you look up specific algorithms. To answer your question, there’s no single resource that I know of for Clojure algorithm implementations — but maybe you should create one? 😉

wombawomba11:07:35

As for BFS and topological sort, it may be helpful to note that BFS/DFS is just recursing over a graph with a stack/queue to hold the state.

wombawomba11:07:00

BTW if there’s a way to state your algorithm recursively (as for BFS/DFS) then that’s generally going to be a lot simpler/more idiomatic to express in Clojure than an imperative equivalent

alpox11:07:51

@UGC0NEP4Y i dont have a general answer but for an example of topo sort in clojure I found stuartsierras library dependency from which I "stole" some structures for a golang implementation lately: https://www.github.com/stuartsierra/dependency/tree/master/src%2Fcom%2Fstuartsierra%2Fdependency.cljc

yuhan11:07:12

Loom is a good library to refer for functional graph algorithms

Joe11:07:51

There's an SICP that uses Python? TIL!

noisesmith13:07:50

@UGC0NEP4Y the bargain one makes when using a language like clojure with immutable data structures is reduced flexibility (the implementation of graph algorithms or even tree algorithms is more convoluted) in exchange for a better set of guarantees about behavior (knowing that you don't have subtle bugs caused by out of order mutations, because you aren't mutating things). The normal trade off is to not use those mutable algorithms, instead using immutable data and a combination of the methods those data structures implement and set operations, lifting what would be mutations into new immutable recursive calls. This is less efficient but easier to do correctly. When one finds bottle necks, you can move over to the tricker but better performing mutable version.

noisesmith13:07:17

I mean, if you want it you can literally do contiguous bytes with pointer operations on the JVM, it performs very well. We avoid it because it is very easy to do wrong.

pinkfrog13:07:05

yup. there should be some dedicated data structures, adt, for those tree/graph operations. While in a mutable world, we just use a queue, a visited set, or vector 2d board (adjacet list/matrix) for those stuffs.

noisesmith13:07:53

sure, and we have queues (clojure.lang.PersistentQueue/EMPTY) sets, and two dimensional vectors

noisesmith13:07:30

it's not hard to make an adjacency list, but yeah, those are great for graphs and it's too bad we don't have a standardized one in the language

noisesmith13:07:50

maybe there's a lib with enough traction to standardize on though

noisesmith13:07:08

and for more perspective, if it isn't clear, our set / hash-map / vector all use a variant of a balanced tree under the hood https://worace.works/2016/05/24/hash-array-mapped-tries/

magra15:07:07

Hi, I just took a look at clojure.spec.alpha source code. It starts with (:refer-clojure :exclude [def]) and then continues with (def *recursion-limit* 4) . What am I missing here? Why can def be used after being excluded and if it can be used after excluding it, what would be the point of doing so?

dpsutton15:07:09

user=> (ns bob (:refer-clojure :exclude [def]))
nil
bob=> (def x 1)
#'bob/x
bob=> x
1
bob=> (defn def [x] :new-thing)
#'bob/def
bob=> (def 3)
Syntax error compiling def at (REPL:1:1).
First argument to def must be a Symbol
bob=> (in-ns 'user)
#object[clojure.lang.Namespace 0x301d8120 "user"]
user=> (bob/def 3)
:new-thing
user=>

dpsutton15:07:03

def is a special form. when when calling def it is always the clojure.core/def special form. however, you can define a def function, and when called with an alias or fully qualified form, its can be resolved

dpsutton15:07:28

and the exclude there is to prevent a warning that you're defining a def when clojure.core/def already exists

dpsutton15:07:16

you can't "not use" a special form because the compiler always handles that for you.

magra15:07:05

Thanks! I forgot about the special form.

dpsutton15:07:01

user=> (let [def :foo] (def {:foo :bar}))
Syntax error compiling def at (REPL:1:17).
First argument to def must be a Symbol
user=>

dpsutton15:07:27

kinda the same thing here. you can create a binding for it but you can't "access" the binding. because the compiler treats def as the special form and doesn't care if there's a local

magra15:07:43

Makes sense once I think about it, but I did not see that one coming.

dpsutton15:07:15

i think its here. its analyzing a seq. if its a special, it analyzes it as a special and the else after that is "look up the function in the first position and go with that"

magra15:07:29

Clojure/script is my first/primary language. Its always baffeling when that java-stuff shows up :-DDD

magra15:07:42

That always feels like living in a shiny spaceship and lifting a sidepanel and its all Kables beneath ;-)

magra15:07:25

Thanks again!

dpsutton15:07:31

in cljs its a bit more straightforward

dpsutton15:07:34

(defn analyze-seq* [op env form name opts]
  (if (contains? specials op)
    (parse op env form name opts)
    (parse-invoke env form)))

dpsutton15:07:13

and

(defmethod parse 'def
  [op env form _ _]
  (when (> (count form) 4)
    (throw (error env "Too many arguments to def")))
...

dpsutton15:07:52

parse is a defmulti that knows how to parse the special forms

magra15:07:00

Nice idea to check it in cljs.

dpsutton15:07:16

cljs is often easier to follow the logic for this kind of stuff

dpsutton15:07:23

its clojure in clojure rather than clojure in java

dpsutton15:07:42

compiler.java vs analyzer.cljs

magra15:07:27

thank you again!

Drew Verlee20:07:13

is there a way to join on a key where it dosn't remove none matches like set/join does?

Drew Verlee20:07:27

merge probably

Drew Verlee20:07:32

i guess it doesn't take a mapper key though

Drew Verlee20:07:45

i can solve this another way though.

noisesmith20:07:35

you could find all items missing from the join, and create artificual null items to join them on from the other side, I guess

hiredman21:07:03

a left outer join