Fork me on GitHub
#beginners
<
2018-05-26
>
KAY_YAK04:05:24

Hi, I am having a little bit of problem with lazy sequence

(require '[clojure.string :as str])

;;READ CUST.TXT
(def my_str(slurp "cust.txt"))
(defn esplit [x] (str/split x #"\|" ))
(def cust (vec (sort-by first (vec (map esplit (vec (str/split my_str #"\n")))))))
;;func to print
(for [i cust] (do (println (str (subs (str i) 2 3) ": [" (subs (str i) 5 (count (str i)))))))


;;CODE TO SEARCH CUST
(defn cust_find [x] (for [i cust :when (= (first i) x )] (str (nth i 1))))
(type (cust_find "2"))

;;READ PROD.TXT
(def my_str(slurp "prod.txt"))
(def prod (vec (sort-by first (vec (map esplit (vec (str/split my_str #"\n")))))))
;;func to print
(for [i prod] (do (println (str (subs (str i) 2 3) ": [" (subs (str i) 5 (count (str i)))))))

;;CODE TO SEARCH PROD
(defn prod_find [x y] (for [i prod :when (= (first i) x )] (nth i y)))
(prod_find "2" 1)

(def my_str(slurp "sales.txt"))
(def sales (vec (sort-by first (vec (map esplit (vec (str/split my_str #"\n")))))))
; (for [i (range(count sales))] (cust_find (nth (nth sales i) 1)))
; (defn fill_sales_1 [x]
;   (assoc x 1
;     (cust_find (nth x 1))))
; (def sales(map fill_sales_1 (sales)))
(def sales (vec (for [i (range(count sales))]  (assoc (nth sales i) 1 (doall (str (cust_find (nth (nth sales i) 1))))))))
; (for [i (range(count sales))] (assoc (nth sales i) 2 (str (prod_find (nth (nth sales i) 2) 1))))
(for [i sales] (println i))
I when I print I get lazy sequence. You can look here https://stackoverflow.com/questions/50538819/lazy-sequence-clojure

seancorfield04:05:19

Hi @kushalkanungo1991 -- Welcome to Clojure! You're stumbling over one of the things that people from an imperative background find most confusing when they are first learning...

seancorfield04:05:37

for in Clojure is not like for-loops in most other languages.

seancorfield04:05:31

If you want to do something with side-effects -- where you essentially throw the result away -- you want doseq:

(doseq [i sales]
  (println i))

seancorfield04:05:35

You're also approaching the overall implementation in a way that suggests an imperative, step-by-step program, using def to "set variables" as you go through the file which is likely to lead you into problems.

seancorfield04:05:42

Try to avoid using def to create Vars that are bound to expressions: those all execute when you load the namespace and it's much better/safer to avoid side-effects when you load a namespace. Make all of them functions, and call them as needed.

seancorfield04:05:13

Also look at let as a way of setting up local bindings of name to expression values (`def` creates global names).

seancorfield04:05:53

There are a lot of places where you're calling vec (and str) that you won't need as you start to understand how sequence functions are used.

_rj_r_04:05:23

Could someone please walk me through how this is calculating the result? It is the apply that is throwing me here:

(defn add-points [& pts]
  (vec (apply map + pts)))
So the result of the call (add-points [5 10] [3 8]) is [8 18]. I'm not sure how mapping the apply is taking the first argument of each vector and adding them, and then the same with the second element of each vector.

andy.fingerhut04:05:46

Does it already make sense to you why (map + [[5 10] [3 8]]) returns (8 18), or would it help to walk through that, too?

_rj_r_04:05:10

honestly, it would probably help to walk through that as well πŸ˜•

_rj_r_04:05:34

although running that in the repl gives a "cannot cast clojure.lang.PersistentVector to java.lang.Number"?

andy.fingerhut04:05:34

So most often you will see map used with just 2 arguments: a function (like +) and a sequence, e.g. (map + [3 5]), which returns the sequence (3 5). Here map is causing the function calls (+ 3) which returns 3 and (+ 5) which returns 5.

andy.fingerhut04:05:07

Oh, sorry, typo on my part: I meant: (map + [5 10] [3 8])

_rj_r_04:05:34

right.. I'm used to using map in instances like map #(+ 10 %) [1 2 3]

andy.fingerhut04:05:32

But map can also take any number of sequences, like this: (map + [5 10] [3 8]). In a case like that, map will make these function calls instead: (+ 5 3) then (+ 10 8), calling the function given to map with the first element of each sequence, then with the second element of each sequence, etc.

_rj_r_04:05:03

ah so map will run each calculation based on the nth index into each collection

andy.fingerhut04:05:49

yes. And it can do that given any number of sequences, e.g. (map + [5 10] [3 8] [-1 2]) return (7 20)

_rj_r_04:05:56

and then I assume the apply is used because this is a nested vector?

andy.fingerhut04:05:56

So add-points has parameters [& pts], where the & means that any number of parameters can be supplied after that point, and they are all collected into a sequence and bound to the local ptr.

andy.fingerhut04:05:37

Without the apply, the call to map would have only 2 arguments, the function + and the sequence pts.

andy.fingerhut04:05:03

With the apply, the call to map now has n+1 arguments: the function + and one argument for each element of the sequence pts.

_rj_r_04:05:13

so the apply unwraps the sequence into individual args?

_rj_r_04:05:41

ah gotcha... thank you! That was a most excellent explanation :thumbsup:

andy.fingerhut04:05:12

Glad it worked. I was making it up as I went along πŸ™‚

andy.fingerhut04:05:33

I mean, the form of the explanation, not the truth of what I was saying.

_rj_r_04:05:12

haha... yeah I got what you meant haha..... you mean you don't have answers to every possible question scripted? πŸ˜›

andy.fingerhut04:05:53

nope, tailor made just for you on the spot.

_rj_r_04:05:25

hopefully I don't have to pay extra for that lol

lepistane09:05:52

how do i use clojure 1.9? this is my lein -v Leiningen 2.8.1 on Java 1.8.0_171 OpenJDK 64-Bit Server VM

lepistane09:05:09

when i lein new app foo i get clj 1.8

jgh09:05:12

need to upgrade to java 9 maybe?

lepistane09:05:25

do i really need java9 for clj 1.9?

jgh09:05:00

you could set your project.clj to use [org.clojure/clojure "1.9.0"] instead of [org.clojure/clojure "1.8.0"]

jgh09:05:39

im not sure why lein is defaulting to 1.8, i usually make my projects manually

lepistane09:05:07

and how do you build them?

jgh09:05:01

with leiningen still, i just dont use it for generating the project

jgh09:05:18

but if you change the clojure dependency to 1.9.0 in the project.clj it should work

lepistane09:05:28

that does work

lepistane09:05:57

i am just looking into templates defaults

lepistane09:05:04

i will post an answer if i find it

jgh09:05:44

there hasnt been a new release since they changed it, it looks like

jgh09:05:54

2.8.1 was Oct 2017, this issue is from Dec 2017

jgh09:05:44

so the next release will have it generate projects with 1.9.0 by default

johnj16:05:37

(read-string "5") or (Long/parseLong "5") ?

lepistane16:05:22

not sure what you are using this for but it's safer option

johnj17:05:14

convert string to number

johnj17:05:44

nothing fancy πŸ™‚

lepistane17:05:59

i am aware of that πŸ˜„ but is string you are using there gotten from some unsafe source - parseLong

πŸ‘ 4
johnj17:05:34

just making sure clojure doesn't have something shorter

johnj17:05:57

java static methods can't be passed to apply?

johnj17:05:20

(apply Long/parseLong ["1" "2"])

rauh17:05:36

No, needs to be a clj function

rauh17:05:14

any fn call in clojure really does obj.invoke() eventually

seancorfield17:05:57

@lockdown- You couldn't apply Long/parseLong to a sequence of strings -- apply unrolls the arguments so your call would be equivalent to (Long/parseLong "1" "2") which is not legal. parseLong can take a second argument -- radix -- but it must be an int.

seancorfield17:05:37

Perhaps you meant map? (map #(Long/parseLong %) ["1" "2"])

johnj17:05:39

yeah, found about that later (realizing my apply mistake)

johnj17:05:51

did exactly that

πŸ‘ 4
seancorfield17:05:04

Just for fun:

user=> (apply #(Long/parseLong %1 %2) ["10" 2])
2
user=> 

johnj18:05:34

this is terrifying, you can pack so much logic into a single 8 line function

johnj18:05:49

wonder if I'll be able to read later

seancorfield20:05:08

@lockdown- That's a valid concern. Reading Clojure gets easier with experience/practice. I would probably write a small wrapper, depending on what I wanted the behavior to be on a parse failure, e.g.,

(defn ->long 
  ([x] (->long 0))
  ([x default]
   (if (number? x) 
     (long x)
     (try
       (Long/parseLong x)
       (catch Throwable _
         default)))))

seancorfield20:05:37

Then

(map ->long ["1" "2"])
or
(map #(->long % ::invalid) ["1" "not-a-number" "2"])

seancorfield20:05:05

(the latter would produce [1 :user/invalid 2] in the REPL)

johnj21:05:41

does the cheatsheet contains everything from core or just the most common used stuff?

seancorfield21:05:49

@lockdown- A test? Well, your choices with parsing are either: throw an exception or return some sort of "invalid" value. The best choice in any given situation depends on what you plan to do with the data. If you want to parse every element in a sequence and just throw away the "bad" ones, returning a flag value is useful, because then it's "just data" and you can do what you want with it. If you throw an exception, you have to decide whether you want to give up all your processing or try to catch that and does something with it etc.

seancorfield22:05:20

If you run (map #(Long/parseLong %) some-large-collection) and the 1,000th element can't be parsed... you get nothing (but an exception). If you return some sort of value instead of throwing an exception, you still have the option of getting those 999+ valid values back.

seancorfield22:05:00

(you could even just return the exception itself -- as a value -- and then filter those out or report on them or whatever)

seancorfield22:05:24

(map #(try (Long/parseLong %) (catch Throwable t t)) ["1" "not-a-number" "2"]) -- then (remove (partial instance? Throwable) ..) would give you just the numbers and (filter (partial instance? Throwable) ..) would give you the exceptions (to report on etc)

seancorfield22:05:07

clojure.core also contains Throwable->map so you can convert an exception into a Clojure data structure for programmatic analysis etc.

andy.fingerhut22:05:56

@lockdown- The cheatsheet currently contains about 85% of the functions, macros, and Vars defined in core. I believe this might be the up to date list of things in core that are not in the cheatsheet: https://github.com/jafingerhut/clojure-cheatsheets/blob/master/src/clj-jvm/TODO.txt#L209-L391

johnj22:05:56

@andy.fingerhut very useful thanks, (and you just cleared the meaning of the list with a commit πŸ˜‰ ) - is there any reason you specified a range in the github link?

johnj22:05:25

@seancorfield convenient, I"ll keep those methods in mind

andy.fingerhut22:05:54

I specified a range because before I did the commit, it was the complete list of things not on the cheatsheet. Now the range is incomplete, but at least it points you in the correct part of the linked file.

πŸ‘ 4
johnj22:05:04

I know there's a performance penalty but is there an idiomatic way to prepend to a vector and maintain the vector?

johnj23:05:44

I'm doing (vec (cons 'foo [vector returned from a fn])

seancorfield23:05:30

@lockdown- If the vector returned from the fn is small, I doubt it will be much of an overhead.

seancorfield23:05:36

FWIW, I used criterium and timed a few options:

user=> (bench (vec (cons 'foo big-vec)))
Evaluation count : 3769620 in 60 samples of 62827 calls.
             Execution time mean : 16.200287 Β΅s
    Execution time std-deviation : 312.821213 ns
...
user=> (bench (into ['foo] big-vec))
Evaluation count : 4092720 in 60 samples of 68212 calls.
             Execution time mean : 14.736989 Β΅s
    Execution time std-deviation : 72.225193 ns
...
user=> 
That's with big-vec being a 1,000-element vector.

alexmiller23:05:05

I think the answer to the original question is - no, it’s not idiomatic to pretend to a vector

seancorfield23:05:19

Hah, true, yes, I missed that @lockdown- wanted an idiomatic way to do it -- I was more focused on the performance πŸ™‚