Fork me on GitHub
#beginners
<
2019-02-28
>
Nazral03:02:38

Ok I'm very confused right now. I made the following function (to be able to turn functions that process one data point and with arbitrary number of arguments before able to process batches of data points)

(defn batcher [f & args]
  (def batch (last args))
  (def beg-args (take (- (count args) 1) args))
  (def new-args (map #(reverse (conj (reverse beg-args)  %)) batch))
  (println (map #(count %) new-args))
  (map #(apply f %) new-args))
And if I comment the println it crashes on some calls telling me that f doesn't get the proper number of arguments, but if I leave the println, then it works

jaihindhreddy-duplicate03:02:06

Step one would be to use let instead of def. let is the way to create local (lexical) bindings.

Nazral03:02:33

Ok thank you, I will do that

Nazral03:02:21

and similarly, with the following function

(defn ngrams-to-bow
  "turn text into bag of words based of dictionary"
  [dict ngrams]
  (def revdict (clojure.set/map-invert dict))
  (def ngram-freqs
    (-> (filter #(contains? revdict %) ngrams)
        (frequencies)))
  (def res (map (fn [word_id] (if-let [x (get ngram-freqs (get dict (str word_id)))] x 0)) (range (count dict))))
  (println (take 10 res))
  res
  )
when I call it in the following context
(def nlabels (count labels))
  (->> (ut/batcher text-to-ngrams min-ngram max-ngram texts)
       (ut/batcher ngrams-to-bow dict)
       (bow-to-tensor)
if I remove the println, bow-to-tensor receives copies of the same bow, but if I keep it, bow-to-tensor gets different (and correct) vectors

jaihindhreddy-duplicate03:02:46

(defn batcher [f & args]
  (let [batch (last args)
        beg-args (drop-last args)
        new-args (map #(reverse (conj (reverse beg-args)  %)) batch)]
    (do (println (type batch)
                 (count batch)
                 (type beg-args)
                 (count beg-args)
                 (map #(count %) new-args))
        (map #(apply f %) new-args))))

seancorfield03:02:50

You don't need do here -- the body of a let is implicitly wrapped in do already.

đź‘Ť 5
Nazral03:02:31

@jaihindh.reddy thank you very much, I changed a few functions using let and it works

Nazral03:02:59

I don't think apply would work because I need the last argument to remain a list and not be considered a continuation of a list of arguments (e.g. (apply + 1 2 '(3 4)) ; equivalent to (apply + '(1 2 3 4)) from the doc)

Nazral03:02:21

In my case, I have many functions with different arities, for example text-to-ngram and it can only process one text at the time, so batcher takes the list of argument + a batch of texts and maps text-to-ngram with the appropriate arguments + individual texts

jaihindhreddy-duplicate03:02:29

I'm unable to understand the intention behind you're function. I have a sneaking suspicion you don't need this function. Check out apply

seancorfield03:02:56

@archibald.pontier_clo If you put beg-args into a vector, you won't need those reverse calls I suspect:

beg-args (into [] (butlast args)
     new-args (map #(conj beg-args %) batch)

seancorfield03:02:25

conj on a vector will append the new item (whereas conj on a list or sequence will prepend it).

seancorfield03:02:23

(but I agree with @jaihindh.reddy that it's very hard to understand what you're trying to do from that code)

jaihindhreddy-duplicate03:02:29

(defn batcher [f & args]
  (let [batch (last args)
        beg-args (drop-last args)
        new-args (map #(reverse (conj (reverse beg-args) %)) batch)]
    (map #(apply f %) new-args)))
user=> (batcher #(println "args: " %&) 1 2 [4 5 6])
(args:  (1 2 4)
args:  (1 2 5)
args:  (1 2 6)
nil nil nil)
This is the same as
(defn batcher [f args-for-each-call args]
  (map #(apply f (conj (vec args-for-each-call) %)) args))
(batcher #(println "args: " %&) [1 2] [4 5 6])

seancorfield03:02:14

which is the same as

(defn batcher [f & args]
  (map #(apply f (conj (vec (butlast args)) %)) (last args)))
if you want to stick to the original calling convention.

âž• 10
Nazral03:02:16

thank you a lot for the help The reason I want this function is because I have many functions with different arities, for example text-to-ngram and it can only process one text at the time, so batcher takes the list of argument + a batch of texts and maps text-to-ngram with the appropriate arguments + individual texts

Nazral03:02:41

What I don't understand is why the let made it work ?

seancorfield04:02:34

def creates a global (top-level) definition each time so it's never what you want inside a function.

seancorfield04:02:29

I think, perhaps, there was also some interaction with laziness which was why having the println made the original work -- but once the function was simplified, and made less imperative, whatever problem was there before went away.

seancorfield04:02:08

It can take a while to go from an imperative mindset to a functional mindset @archibald.pontier_clo -- may I ask what other languages you've used before Clojure?

Nazral04:02:58

well for data science mostly scala and python, for personal projects some OCaml

Nazral04:02:23

and I see thank you, I wasn't aware def is global

seancorfield04:02:57

OK, so you've probably had some exposure to functional programming style already in Scala and OCaml, although both of those support imperative and object-oriented styles too -- both of which are a lot less idiomatic in Clojure.

seancorfield04:02:57

(defn foo [args] body) is a shorthand for (def foo (fn [args] body)) and both define global (top-level) Vars.

seancorfield04:02:36

Vars can be treated as (global) mutable "variables" but it's not idiomatic (and not recommended).

seancorfield04:02:56

Clojure is a lot more "opinionated" than a lot of other languages 🙂

seancorfield04:02:36

I did Scala for about 18 months before I came to Clojure, and I've done a bit of F# which I gather is similar to OCaml.

Nazral04:02:30

It will take some getting used to it, that being said I quite happy with the language so far. To discover it I decided to port one of our machine learning service to clojure this week and it has been relatively painless and the code is much clearer than our python version

seancorfield04:02:27

@archibald.pontier_clo That's great to hear! Hopefully you'll find the community is really helpful and welcoming. We're certainly very passionate about Clojure 🙂

Nazral07:02:50

Is there a better way to do (remove nil? (map ...)) than this ? (I'm thinking about flatMap in Scala)

Nazral09:03:45

thank you !

Lennart Buit07:02:12

flatMap doesn’t sound the same to me, or at least in JavaScript it will flatten nested arrays one level

Nazral07:02:50

scala> val strings = Seq("1", "2", "foo", "3", "bar")
strings: Seq[java.lang.String] = List(1, 2, foo, 3, bar)

scala> strings.map(toInt)
res0: Seq[Option[Int]] = List(Some(1), Some(2), None, Some(3), None)

scala> strings.flatMap(toInt)
res1: Seq[Int] = List(1, 2, 3)

Nazral07:02:21

But I guess the option type can be seen as a form of nesting

Lennart Buit07:02:30

yeah, that was what I was thinking

Lennart Buit07:02:30

But there is (keep f coll), that keeps all values in coll for which f did not return nil

Nazral07:02:07

I really want to keep the return values of f

Lennart Buit07:02:21

Right, my explanation was shotty, f is also applied, but if f returned nil, the result is removed from the (new) collection

Lennart Buit07:02:56

Far sharper minds than mine can explain better: https://clojuredocs.org/clojure.core/keep

Nazral08:02:29

I have my service that is working fine which loads a config file that is in resources as well as a bunch of other files, would it be possible to have one jar containing everything, using lein?

Nazral09:02:42

Nevermind, it is possible, will make it work !

geofftate10:02:42

good day all

geofftate10:02:06

i'm working on this excercise:

An Armstrong number is a number that is the sum of its own digits each raised to the power of the number of digits.

For example:

    9 is an Armstrong number, because 9 = 9^1 = 9
    10 is not an Armstrong number, because 10 != 1^2 + 0^2 = 1
    153 is an Armstrong number, because: 153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153
    154 is not an Armstrong number, because: 154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190

Write some code to determine whether a number is an Armstrong number.
And here's what I've come up with:
(ns armstrong-numbers)

(defn exp [x n]
  (reduce * (repeat n x)))

(defn armstrong? [num]
  (let [numStr (String/valueOf num)
	    numLen (count numStr)
	    nums (seq numStr)
		expn (fn [x] (exp (Character/getNumericValue x) numLen))]
    (= (reduce + (map expn nums)) num)))
This my first clojure code that is not a single expression. Had to ddg (duckduckgo) a lot.

geofftate10:02:51

let feels strange coming from an imperative style

restenb13:02:57

looks good to me, @thirdy.derivera. numLen can probably be just (-> num str count), also I believe there's a power function in clojure.math for handling exponentials

đź‘Ť 5
borkdude13:02:35

(Math/pow 2 3) => 8.0

Mattias14:02:51

Hey, I’m on Mac OS and after a brew upgrade I can’t run “lein repl”. I get a permission denied back, saying to check my ...repl-port. Leiningen was updated to 2.9.1...

Alex Miller (Clojure team)14:02:04

might help to ask in #leiningen and include the full error

Mattias14:02:15

Doh. Thanks!

Pwn15:02:55

Awesome, looks like people are actually on this chat.

Pwn15:02:47

Question, when it comes to clojure, are there frameworks to help out with IOC?

Pwn15:02:08

Is it actually something that is used? If not, how do you write unit tests in Clojure without them turning into integration tests, or methods that don't require you to continually pass in your dependencies requirementsn?

borkdude15:02:37

you can write unit tests by using a (embedded/in memory) database populated with test data, no need to mock a DB that way. for other services you can mock something. personally I favor writing integration tests, need them anyway, less work than doing both and catches the errors I’m interested in.

borkdude15:02:01

I do write unit tests for more data oriented/pure stuff

borkdude15:02:03

also you can use with-redefs to mock functions, or use mock components (using the before mentioned library)

Pwn15:02:29

I want to test a function, just that function and none of it's dependencies, or I want the ability to change what that function does for whatever reason

Pwn15:02:04

As a hypothetical, I mean

Pwn15:02:42

Following that line of thinking, I would have to pass in the functions dependencies instead of using the statically defined functions in the rest of the language, correct?

borkdude15:02:23

not necessarily. see with-redefs.

lilactown16:02:11

integrant is another popular framework for that

Pwn16:02:49

awesome, thanks!

Charlot18:02:36

did something happen to usering :refer :all in 1.10?

Charlot18:02:42

it seems there is a spec for require that now means you can only use a sequence of symbols

ghadi18:02:59

paste your ns form?

Charlot18:02:13

Okay, I got it work, but it's been finicky. Sorry for the standup

ghadi18:02:19

(require '[clojure.instant :refer :all]) works just fine

ghadi18:02:32

similarly for :require within an ns

dpsutton18:02:40

possibly clojurescript?

borkdude19:02:25

true:

cljs.user=> (require '[clojure.set :refer :all])
Unexpected error (Error) compiling at (<cljs repl>:1:1).
:all is not ISeqable

đź‘Ť 5
borkdude19:02:47

that never worked though, it’s not related to spec