Fork me on GitHub
#beginners
<
2017-12-16
>
Alex Miller (Clojure team)00:12:09

cl-format has everything - prob some way to do it there

leira01:12:50

boot.user=> (take 10 doubles) (1 2 4 8 16 32 64 128 256 512)

leira01:12:57

How exactly does this code work?

derpocious02:12:12

Hi, I'm using cljs.core.async, and I'm wondering if it's possible to print everything in a channel without actually taking the things out of it.

jgh02:12:50

(def doubles (lazy-seq (cons 1 (map (partial * 2) doubles)))) seems a bit clearer

jgh02:12:45

seems like it creates a lazy sequence that adds 1 to the beginning (via cons) of itself, and multiplies the rest of the elements by 2 (via map).

jgh02:12:31

actually the order of that is the other way round, the map happens first and then 1 is added to the front.

jgh02:12:21

it’s recursive, so when you do (take 10 doubles) or (nth doubles 10) it is recursed 10 times

leira02:12:38

I actually prefer this version (def doubles (cons 1 (lazy-seq (map (partial * 2) doubles))))

noisesmith04:12:27

that lazy-seq call doesn't do anything, map already returns a lazy seq

jgh02:12:50

yeah i agree, i find the thread-last macro makes things a bit more confusing.

leira02:12:16

So lazy-seq returns a LazySeq object, which contains a fn inside it, which will be called when this item is realized, right?

jgh02:12:47

seems so. I’m pretty new myself so I don’t want to give you a definite ‘yes’ here, but that’s the way it seems to make sense.

leira02:12:15

so the first element is 1, in the final version, apparently~

leira02:12:29

the second element is a LazySeq created by lazy-seq

jgh02:12:36

yes, I think as long as it has that (cons 1) the first element will always be 1

leira02:12:56

when the second element is realized, (map (partial * 2) doubles) is called

leira02:12:38

map created a LazySeq by map itself

jgh02:12:41

yes because it is (cons 1 [1 * 2])

leira02:12:52

when first element of (map (partial * 2) doubles) is requested, it tries to realize the one step, it takes the first element from doubles, and doubles it and returns as the new element

leira02:12:37

when second element of the (map ...) is requested, it takes the second element from doubles and doubles it and return.

jgh02:12:20

yes it takes the result of the recursive call below it and maps the multiplication to all elements in that sequence, then it adds 1 to the front, then it returns that sequence to the recursive call above it, so the sequence is now one bigger.

jgh02:12:44

and take, nth, etc allow the recursion to get n deep

jgh02:12:44

so the 10th (Deepest) call is '(1), 9th is '(1 2), 8th is '(1 2 4), etc..

jgh02:12:30

i guess nth does n+1 since it starts from 0 because it’s an index.

seancorfield02:12:30

Ah, didn't realize you'd asked the same questions here and in #clojure @leira

leira02:12:38

😉 sorry, I didn't got answer from here, so I copied the question in #clojure again...

seancorfield02:12:38

(we usually suggest patience rather than cross-posting your question to other channels -- it's Friday evening here in the USA so a lot of Clojurians have gone home for the weekend... 🙂 )

leira02:12:05

yeah u r right I will try to be patient next time~

seancorfield02:12:06

And welcome to Clojure by the way! 🙂

leira02:12:27

thx writing in Clojure is so enjoyable

matan10:12:33

Transients: Why would I use a transient? Or rather, if they share their data structure with the original persistent data structure that they are instantiated from, how exactly are they more performant than instantiating copies of the original data structure with immutable steps, like normal clojure data structures are used? In my limited mind the tutorial seems to side-step these essentials https://clojure.org/reference/transients

didibus05:12:31

Code without transient, and when you've got a reason to need to be even faster, if you are doing a lot of edits on vectors/maps or sets, then try transients.

didibus05:12:02

Their benefits come from allocating less intermediate garbage.

matan18:12:33

@didbus thanks, I'm almost on top of it now, and will augment with that resource

matan10:12:12

Is it purely an instantiation overhead, that we are eliding here?

sb13:12:45

(def A (apply vector (range 1000)))
(for [k (range (count A))]    
    (for [i (range (- (count A) k)) :let [j (+ i k)]]
            (apply max (subvec A i (+ j 1)))))

sb13:12:06

Could you help me how to optimise this code?

sb13:12:00

I tried with doseq or do parallel with await agents.. not helped. Maybe with recur?

sb13:12:18

task https://www.hackerrank.com/contests/world-codesprint-12/challenges/max-transform *just I dont know how to optimise within 8 sec run.. with min 4000 length A vector

leonoel13:12:38

@sb if your input is sorted, the max operation can be replaced by a constant-time vector access

sb14:12:37

Unfortunately not constantly sorted.. so I dropped this way now. By the way, that isn’t real life problem.. so I dropped now. Later I will check it.

leonoel14:12:23

sorting a sequence is O(n.log(n)) and your algorithm is O(n²) so it doesn't increase complexity to start by sorting your input

leonoel14:12:39

that's why sorting is so fundamental in CS : many algorithms are faster if they assume sorted input

leonoel14:12:41

and I guess the purpose of this exercice is to teach this

sb14:12:26

Yes, that is true! 🙂

sb13:12:37

@leonoel thanks I check it. I don’t understand fully what you wrote, but I research this kind of constant-time vector access. (pop peek? if I understand good)

leonoel13:12:23

yes, peek instead of apply max will save a vector traversal

leonoel13:12:51

or just (get A j), subvec is not even needed

sb13:12:19

Ok, I rewrite the code based on this. Thanks the advices!

derpocious17:12:09

Hi, I'm using cljs.core.async, and I'm wondering if it's possible to print everything in a channel without actually taking the things out of it.

derpocious17:12:26

Is there a function that can do that

derpocious18:12:06

I guess it's kind of like a queue. You can't just print the whole thing?

noisesmith18:12:28

it's not possible, no

nimblerabit20:12:33

I have some symbols defined in my code, e.g. (def myThing1) (def myThing2), and I'm trying to assign a map value to one of those defs later based on user input. So if the user inputs "myThing2", a value in my map becomes that defined piece of code. Is there some way to do this without just a big cond?

gklijs20:12:25

You could make a function that takes a string, and creates a symbol, using the symbol function, if they all use the same namespace it’s very simple

noisesmith21:12:52

if something uses user input to pick among options, it's safer to make a hash-map from string to the specific options

noisesmith21:12:26

using symbol / resolve is much riskier

noisesmith21:12:22

I like using a hash-map rather than case because you can explicitly define a data literal describing the things a user could look up

justinlee21:12:17

Just out of curiosity, how does one “dereference” a symbol from a string. I know you can make a symbol with (symbol "myThing1") but I couldn’t figure out how you get its value.

noisesmith21:12:31

resolve - that's how you let a user hack your app

noisesmith21:12:49

it allows user strings to execute arbitrary code, pretty much never what you want

justinlee21:12:16

Is that clojure only? In cljs it says

cljs.user=> (resolve (symbol "thing1"))
            ^
Assert failed: Argument to resolve must be a quoted symbol
(core/and (seq? quoted-sym) (= (quote quote) (first quoted-sym))) at line 1

noisesmith21:12:30

oh, that's different - clojurescript is much more limited

noisesmith21:12:44

just use an explicit hash-map lookup it's a lot simpler

justinlee21:12:57

oh yea that’s clearly the “right” way to do it, I was just curious

noisesmith21:12:32

in clj

Clojure 1.9.0
+user=> ((resolve (symbol "+")) 2 2)
4

noisesmith21:12:21

if you want even more user power:

+user=> (eval (read-string "(System/exit 0)"))
+justin@localhost:~$ 

noisesmith21:12:54

that's doing the RE of repl by hand

justinlee21:12:14

Interesting. Your example works the way I expected in clojure, but mine still doesn’t.

(def thing1 "hi")
(println "thing1 = " (resolve (symbol "thing1")))
prints
thing1 =  #'user/thing1

justinlee21:12:46

I thought that would print thing1 = hi

noisesmith21:12:23

@lee.justin.m oh I used a trick - if you call a var the var calls its own value

noisesmith21:12:28

use deref (aka @)

noisesmith21:12:46

@(resolve (symbol "thing1"))

noisesmith21:12:19

there's also var-get but deref does the same thing on a var and has a handy shorthand

justinlee21:12:32

yea that works. okay that makes sense

noisesmith21:12:02

but things are different in clojurescript - usually your cljs app isn't a self-hosted cljs

noisesmith21:12:20

and things are not reified in cljs in the exact ways they are in clj

noisesmith21:12:52

and all that aside, it's safer and less error prone to explicitly look up a value provided by the user in a collection made for that purpose

noisesmith21:12:32

I would do it like this

(def options                                                                    
  {"option1" thing1                                                             
   "option2" thing2})                                                           
                                                                                
(let [user-option (get options user-input)]                                     
  (user-option arg))

nimblerabit21:12:18

I'll take your suggestion and do it that way, I wasn't worried about safety but it isn't too difficult and seems nicer anyways

noisesmith21:12:42

of course if you want to be clever that let block can be replaced by ((options user-input) arg)

nimblerabit21:12:32

I'm having another issue now where after user makes a selection (in the console) I use a block like (do (run-some-fns-and-print) (input-loop)) which is supposed to print some stuff out and then restart my main input-loop

nimblerabit21:12:06

but the print side effect from the first call in my do isn't working, nothing prints and instead the input loop just runs again

noisesmith21:12:15

that would blow the stack eventually

noisesmith21:12:33

this is cljs, right?

noisesmith21:12:06

are you using print or println?

nimblerabit21:12:24

the print function looks like this:

nimblerabit21:12:28

(defn print-reachability
  [board]
  (->> (to-nested-vectors board)
       (map clean-vector)
       (map println)))

noisesmith21:12:42

OK yeah that's never going to do anything

noisesmith21:12:54

map is lazy, it does nothing at all if the output result is not consumed

noisesmith21:12:16

use run! on the line with println and that will fix it

noisesmith21:12:47

if you used print-reachability in the repl, the map call would be forced because the repl forces it in order to print it

nimblerabit21:12:58

ahhh, yes I was testing it in the repl

noisesmith21:12:55

Clojure 1.9.0
+user=> (do (map print (range 10)) (flush))
nil
+user=> (do (run! print (range 10)) (flush))
0123456789nil

noisesmith21:12:19

the do ensures that the repl doesn't consume the first arg

nimblerabit21:12:34

That worked, thanks!