Fork me on GitHub
#beginners
<
2018-09-19
>
borkdude11:09:56

About uberjar/running from git… I tried a lumo project recently with npm dependencies. It was a extra effort to turn this into a single deployment artifact (I did manage to) and I learned that many people just run npm/yarn install and then start their project from that directory, so I just did that instead.

jmp13:09:52

Hi! I have an extremely basic question. Do I need any HTML, CSS, or JavaScript knowledge if I want to get into ClojureScript? and if so are there any resources to recommend? Thanks! 🙂

mfikes13:09:21

@juanmp If you want to learn the ClojureScript language, no. (ClojureScript isn’t tied to web tech.)

mfikes13:09:44

Grab Lumo or Planck and just start learning.

mfikes13:09:38

You can largely ignore the JavaScript aspect for quite a while if you aren’t interoperating with any JavaScript libs or doing other platform-specific interop.

ben14:09:47

I have a similar question: are there any really minimal clojurescript example web apps. I’m thinking something analagous to https://github.com/abishekaditya/elm-counter

ben14:09:55

looks nice, thanks

grierson14:09:48

I recommend reading the re-frame documentation it's very good. https://github.com/Day8/re-frame/blob/master/README.md

grierson14:09:17

They use a very similar pattern to Elm if you are familiar with that pattern.

ben14:09:21

I found this (https://gist.github.com/daGrevis/c55395a3e85a07ea6973) but I was wondering if there was anything simpler, or potentially react-less

jmp14:09:33

I’ve also been recommended re-frame as something to check out to get started with it, what’s your thoughts on it?

mfikes14:09:42

If you are interested in building UIs (web or React Native), IMHO, re-frame is the simplest and most popular way to go.

jmp14:09:02

Awesome, thank you so much for your input 🙂

Denis G15:09:06

is there an infinity constant in clojure

Denis G15:09:09

Double/POSITIVE_INFINITY ?

Alex Miller (Clojure team)15:09:43

(relatedly, ##-Inf, ##NaN)

andy.fingerhut15:09:45

Starting with Clojure 1.9, I think.

sb16:09:16

Possible run parallel in emacs/cider clj and cljs repl? Because when I start clj after this a send command cljs-start and i see just the cljs repl. If i start a new clj session, that isnt a same session data. Now i jump everytime from cljs to clj and ... is there something easier with emacs?

noisesmith16:09:07

How do your backend server and frontend page share the same state? If you can solve that you can connect to both as separate repls, if you can't that's an application architecture problem not a tooling problem.

sb16:09:14

I have frontend cljs and clj backend. I send data to clj part and work with that. I just want to work more dinamically.

sb17:09:04

Yes, I would like to check the data flow

roosta17:09:34

Could M-x cider-jack-in-clj&cljs be what you're looking for?

👍 4
sb20:09:25

Hmm interesting! I check it now!! Thx!!

sb21:09:25

@rosta 👍👏 works

Fahd El Mazouni16:09:41

Hi ! I need help with a little thing. So what I'm trying to do is to parse logical expressions like A + (B | D) etc.. I'm using regex, but I'm getting some weird error. I'm probably missing something any help would be much appreciated (snippet incoming)

Fahd El Mazouni16:09:47

(translate-expr "C | (C ^ E)") works as I would expect it

Fahd El Mazouni16:09:05

but (translate-expr "(C + D) | (C ^ E)") fails

Fahd El Mazouni16:09:48

returns null (not nil) java nul pointer exeption in Matcher on getlenght, but I don't see where null might get passed to the matcher

hiredman17:09:35

I would put a println right after the let binding printing out the value of right operator and left

hiredman17:09:47

one of those is likely nil

hiredman17:09:51

but, assuming your logical expression grammar is recursive, regexs aren't going to work great for parsing it

☝️ 8
hiredman17:09:42

instaparse might work well

☝️ 4
❤️ 4
3Jane17:09:42

(it’s possible to make recursive regexes etc, but my recent experiences show that 1) the more advanced options don’t necessarily work in all languages, and 2) the more complicated options are painfully slow)

Fahd El Mazouni17:09:18

thanks for the tips ! I'm refraining from using any libraries that might make life easier for me (it 's for a school project and I want to maximize my learning)

Fahd El Mazouni17:09:52

any tips for how to implement a proper parser ? ( maybe I'll just try to reimplement instaparse 😛 )

rmprescott23:09:03

A homework assignment probably had a simple enough grammar that this classic approach will work https://en.m.wikipedia.org/wiki/Recursive_descent_parser

Denis G17:09:08

Do you know when his could happen:

CompilerException java.lang.ClassCastException: clojure.lang.LazySeq cannot be cast to clojure.lang.IPersistentStack,

3Jane17:09:20

@fahd.elmazouni jar of salt needed because I haven’t read through this yet myself, but you might find this of interest: http://craftinginterpreters.com/contents.html

hiredman17:09:43

@denisgrebennicov using peek or pop on a lazy seq

Denis G17:09:41

@hiredman thanks! I was using peek because it looks better, but yeah ...

Fahd El Mazouni17:09:06

I thought of using stacks might end up just doing that 😛

Fahd El Mazouni17:09:28

I'm building an inference engine with logical expressions as rules

Denis G17:09:31

how to compare two Double/POSITIVE_INFINITY ? Can not use ##Inf because of clojure version being not 1.9

Denis G17:09:50

Example:

(def l ['(Double/POSITIVE_INFINITY 4 3) '(5 4 2)])
(->> l
  (map (fn [stack] (if (= (peek stack) Double/POSITIVE_INFINITY) stack (rest stack)))))

Denis G17:09:11

I want to return rest from the subarray/stack if top is not infinity

Denis G17:09:15

oohh damn, just use keyword 😄

hiredman17:09:30

you need to not quote Double/POSITIVE_INFINITY

noisesmith17:09:43

using ' to make lists is weird

andy.fingerhut17:09:23

(= [Double/POSITIVE_INFINITY 2 3] [Double/POSITIVE_INFINITY 2 3]) evaluates to true. Is that not working?

hiredman17:09:53

Double/POSITIVE_INFINITY is a symbol, that when evaluated is the positive infinity value for doubles, but quotation means it isn't evaluated, so it is the symbol

andy.fingerhut17:09:19

ah, missed that part of Denis G's question.

Denis G17:09:52

Thank you! 😅

Denis G17:09:25

but would a keyword be a better use here

Denis G17:09:38

if I want to compare keyword with number it throws an exception

Denis G17:09:44

better check for keyword?

Denis G17:09:53

it's like a 'pseudonode' if you wish

andy.fingerhut17:09:27

Are you asking whether it would be better to use a keyword like :my-custom-infinity-keyword instead of Double/POSITIVE_INFINITY?

andy.fingerhut17:09:27

It depends on your use case, but note that (unquoted) Double/POSITIVE_INFINITY has type Double. You can do at least limited kinds of arithmetic with it and get back Double results.

andy.fingerhut17:09:35

You can't do arithmetic on keywords

andy.fingerhut17:09:03

If you don't need to do arithmetic on this part of your data, then that distinction isn't important.

jaawerth17:09:08

there's a "times double infinity" joke in there somewhere

Denis G17:09:48

in this case +infinity is just a flag on the top of the list, so that I don't pop elements from there, in theory it could be anything, I just put +infinity, because it was the first thing I have though (and would do it in Python, Java, C++) The question is, if it is more a Clojury way to use a keyword instead

andy.fingerhut17:09:30

If the reason for having that marker there has nothing to do with the Double value +infinity, then yeah, a keyword that you choose for this purpose sounds reasonable.

andy.fingerhut17:09:44

There are a few libraries that wish to allow all kinds of values through a 'channel' or 'queue' of some kind, and they sometimes go to the trouble of creating a brand new Java Object that no other part of the program has access to, and compare against that value. A value used for that purpose is often called a sentinel. Not sure if that is your use case here, just wanted to mention it in case you were ever worried that your new keyword might be part of the data you want to process sometimes.

andy.fingerhut17:09:34

Even if so, you can create a keyword with a namespace that is part of your application's namespaces, and it maybe becomes unlikely enough to worry about that any data you were processing used that same keyword.

Denis G17:09:52

Thank you!

Denis G17:09:49

Dunno whether this is beginner or puzzles question, gonna ask anyway. Solving the hackerrank problem using clojure. Finally all good and ... Timeout Problem: https://www.hackerrank.com/challenges/poisonous-plants/problem?h_l=interview&amp;playlist_slugs%5B%5D=interview-preparation-kit&amp;playlist_slugs%5B%5D=stacks-queues PS. idea is correct 😉 Question is, how can I speed up this code:

(defn build-stacks [coll]
  (reduce (fn [stacks val]
              (let [top-stack (peek stacks)
                    top-elem (peek top-stack)]
                  (if (> val top-elem)
                      (conj (rest stacks) (conj top-stack val))
                      (conj stacks (list val)))))
    (list (list (first coll)))
    (rest coll)))

(defn merge-stacks [coll]
    (reduce 
        (fn [stacks other-stack]
            (let [top-stack (peek stacks)
                  last-stack-val (last top-stack)
                  top-other-stack-val (first other-stack)]
                (if (>= last-stack-val top-other-stack-val)
                    (conj (vec (drop-last stacks)) (concat top-stack other-stack))
                    (conj stacks other-stack))))
        [(first coll)]
        (rest coll)))

(defn poisonous-plants [coll]
  (loop [days 0 stacks (merge-stacks (build-stacks (reverse (concat [Double/POSITIVE_INFINITY] coll))))]
      (if (= 1 (count stacks))
          days
          (recur (inc days) (->> stacks
                                 (mapv (fn [stack] (if (= (first stack) Double/POSITIVE_INFINITY) stack (rest stack))))
                                 (filterv (comp not empty?))
                                 (merge-stacks))))))

Denis G19:09:30

The problem was/using concat and last in merge-stacks function. Now all I do is with vecs. Only one test-case timeouts...

(defn peekv [coll]
  (nth coll (dec (count coll))))

(defn restv [coll]
  (subvec coll 0 (dec (count coll))))

(defn build-stacks [coll]
  (reduce (fn [stacks val]
              (let [top-stack (peekv stacks)
                    top-elem (peekv top-stack)]
                  (if (> val top-elem)
                      (conj (restv stacks) (conj top-stack val))
                      (conj stacks [val]))))
    [[(first coll)]]
    (rest coll)))

(defn merge-stacks [coll]
  (reduce 
      (fn [stacks other-stack]
          (let [top-stack (peekv stacks)
                last-stack-val (first top-stack)
                top-other-stack-val (peekv other-stack)]
              (if (>= last-stack-val top-other-stack-val)
                  (conj (restv stacks) (apply conj other-stack top-stack))
                  (conj stacks other-stack))))
      [(first coll)]
      (rest coll)))

(defn poisonous-plants [coll]
  (loop [days 0 stacks (merge-stacks (reverse (build-stacks (conj ((comp vec reverse) coll) Double/POSITIVE_INFINITY))))]
      (if (= 1 (count stacks))
          days
          (recur (inc days) (->> stacks
                                 (mapv (fn [stack] (if (= (peekv stack) Double/POSITIVE_INFINITY) stack (restv stack))))
                                 (filterv (comp not empty?))
                                 (merge-stacks))))))

enforser19:09:54

@denisgrebennicov I took a shot at solving this, wondering how it might compare in time

enforser19:09:59

(defn- elim-plants
  "Eliminates the plants for one day - returns list
  of remaining plants."
  [plants]
  (let [plants (concat [(first plants)] plants)
        tuples (partition 2 1 plants)]
    (keep (fn [[left right]]
            (when (>= left right)
              right))
          tuples)))

(defn poisonous-plants
  "Removes plants for each day - stopping when the number of plants
  is the same as the previous day, because that indicates no more will
  die. Returns number of days (iterations) until stopping point."
  [plants]
  (loop [prev-count -1
         plants plants
         days 0]
    (if (= (count plants) prev-count)
      (dec days)
      (recur (count plants) (elim-plants plants) (inc days)))))

(deftest plant-test
  (is (= 0 (poisonous-plants [])))
  (is (= 0 (poisonous-plants [1 1])))
  (is (= 0 (poisonous-plants (take 1000 (repeat 1)))))
  (is (= 1 (poisonous-plants [1 2])))
  (is (= 1 (poisonous-plants (take 1000 (iterate inc 1)))))
  (is (= 2 (poisonous-plants [1 3 2]))))

enforser19:09:59

I timed our solutions with my unit tests, and mine takes about 2-3ms to run, while yours takes 80ms 😮

Denis G19:09:59

~80ms? The first or the second one? 😄 That was it? Your code is so short 😄 😢

enforser20:09:13

oh, i didn't try the second one! I'll try it

Denis G20:09:58

😄 the problem was with concat and last of course which are linear in time

enforser20:09:04

now it's around 30ms!

enforser20:09:38

you can time things with time by the way... (time (plant-test))

Denis G20:09:47

BTW i don't think partition 2 1 is good for large inputs, dunno 😄

Denis G20:09:29

your solution 😛

Denis G20:09:24

i mean it's clear. But "naïve" O(n^2) solution. I tried to do as less iters as possible

Denis G20:09:06

here's the idea i tried to implement

Denis G20:09:45

this is why I have this build-stacks function The idea is build-stacks while count of stack is > 1 pop from every stack other than first remove empty stacks merge mergable stacks repeat

peter-kehl19:09:47

Hi. I'm new to namespaces. How do I load thrown? macro, please? In clj 1.9.0 REPL: (use '[clojure.test]) assert-expr evaluates, but then thrown? fails as an unknown symbol.

andy.fingerhut19:09:14

Not sure off top of my head, but thrown? might not be an independent macro, but a special symbol recognized within the context of a clojure.test (is ...) expression. Are you trying to use thrown? inside such an expression, or independent of such?

👍 4
dpsutton19:09:11

correct. there's a defmulti in there about how to interpret an is expression.

andy.fingerhut19:09:27

Yeah, so after use'ing clojure.test, you can write expression like (is (thrown? ...)), but (thrown? ...) by itself not inside of is isn't defined

Nikita Vasilchenko19:09:32

Hi! After executing lein test I got this stack trace. But I can’t understand where to find string number in which this exception throwed.

mbjarland19:09:50

I’m trying to find the most concise way of writing the following function:

(defn format-duration [duration]
  (if (< duration 1)
    "now"
    (let [f #(str %1 " " %2 (if (< 1 %1) "s" ""))
          [_ xs] (reduce
                  (fn [[d r] [k v]]
                    (if (= 0 (quot d v))
                      [d r]
                      [(mod d v) (conj r (f (quot d v) k))]))
                  [duration []]
                  {"year" 31536000 "day" 86400 "hour" 3600 "minute" 60 "second" 1})]
      (condp = (count xs)
        1 (first xs)
        2 (str (first xs) " and " (last xs))
        (let [ys (interpose ", " xs)]
          (apply str (concat (butlast ys) ["and " (last ys)])))))))

which prints a number or seconds as “human readable”, example:
(format-duration 35125)
=> "9 hours, 45 minutes, and 25 seconds"
where the human readable format follows english language list rules (including the oxford comma). Any suggestions for improvement much appreciated (where improvement in this case is defined closer to code golfing than readability)

andy.fingerhut19:09:47

@xenmayer When you see a stack trace like that, often you want to skip over any lines mentioning clojure.core, and look for namespaces or function names that are yours, e.g. is csvdb.core a namespace of yours that contains a function order-by* ? If so, look at line 10 of that output.

👍 4
andy.fingerhut19:09:10

Also, avoiding the file name core.clj for your own code can help your file names "stick out" more easily from clojure.core's file with the same name.

andy.fingerhut19:09:28

I know that Leiningen templates often create such a file for you, but you can delete it.

andy.fingerhut19:09:10

Unfortunately the stack traces only give the base file name, not a path.

Mario C.20:09:25

Is there any gotchas when using a let binding inside an as-> thread?

Mario C.20:09:56

(as-> (let [test (somefn)] (somefn test)) $ ...)

noisesmith20:09:26

@mario.cordova.862 as-> expands to a let block, I wouldn't expect it to break any more than nested lets would (and those work fine)

peter-kehl20:09:38

Thanks @andy.fingerhut and @dpsutton. Confirming:`thrown?` is recognised within is. However, it doesn't seem to "work." For example, [(assert true) 1] returns [nil 1]. [(is true) 1] returns [true 1]. Then wouldn't you expect [(is (thrown? ArithmeticException (/ 1 0))) true] also to return a 2-item vector? But it lists the exception and it doesn't return anything - so it seems not to handle the exception. (https://clojuredocs.org/clojure.test/is#example-542692d7c026201cdc327116)

noisesmith20:09:28

using is for return value seems weird to me and likely to hit corner cases

noisesmith20:09:52

it alters a dynamically bound value, and is meant to interact with other code that uses that value

andy.fingerhut20:09:18

Agreed. I haven't seen any project that uses the return value of (is ...) forms before.

andy.fingerhut20:09:30

That isn't to say there is no valid way to do that, but you are in far-less-charted waters there, and you can very likely find a way back to the safer parts of the map that doesn't need to do that.

peter-kehl20:09:12

Thanks. Yes, it was for theoretical purpose (http://www.4clojure.com/problem/65). I worked around.

rmprescott23:09:26

Be sure to look at adereth's solution to that one. Short and sweet

dpsutton21:09:12

what a strange question

dpsutton21:09:20

(not you, the 4clojure one)