Fork me on GitHub
#beginners
<
2018-10-22
>
_rj_r_00:10:46

so I'm working through some Hackerrank problems, the one I am working on is to compute e^x for the first 10 terms with a given value x The following is my solution, it works just fine, but I am curious if someone can let me know how this can be simplified. In addition, as per the problem description, def/defn are not allowed (for some stupid reason).

andy.fingerhut00:10:32

reduce is necessary when later iterations depend upon values calculated in earlier ones, but here it looks like there is no dependency, so map would also work.

andy.fingerhut00:10:08

I don't see e mentioned anywhere in that code, so not sure how it could be working

_rj_r_00:10:38

it is calculating the first 10 terms in the series expansion for e^x

_rj_r_00:10:42

I'm not quite clear how to use map here, can you explain?

andy.fingerhut00:10:44

reduce could be taken advantage of to reduce the number of arithmetic operations required, I believe, since the N-th term appears to be equal to the (N-1)-th term divided by N, or something similar to that.

andy.fingerhut00:10:04

(map (fn [v]
       (/ (Math/pow x v)
          (reduce * (range 1 (inc v)))))
     (range 0 10))

andy.fingerhut00:10:02

I believe the reduce part of your code is equivalent to the map expression above. Note that your res arg is not used anywhere.

andy.fingerhut00:10:19

except to conj onto the end of a vector

_rj_r_00:10:28

hmmm right.. my thinking was it was building a vector which is again reduced with addition. But I see your point.. trying to figure out it now.. basically that reduce is superfluous

andy.fingerhut00:10:28

Changing that to map makes the code smaller and for many people, simpler to understand its intent.

andy.fingerhut00:10:10

As mentioned above, if you find a way to calculate the (N+1)-th term from the N-th term with fewer arithmetic operations, then reduce could be taken advantage of to implement that.

_rj_r_00:10:19

using map there is quite a bit cleaner. Thank you! This is why I wanted feedback 🙂

CyberSapiens9703:10:25

where is the proper place/folder to put your data spec in leiningen?

andy.fingerhut04:10:01

I don't think there is only one proper place -- it can be put in any source file you want.

CyberSapiens9704:10:08

alright, thanks

seancorfield04:10:04

@cybersapiens97 At work we tend to put our data specs in their own namespaces. If there are any functions in with them, they tend to be just predicates used in the specs and perhaps some utilities that operate on the data. But that's just our "tendency" and we break those rules if it makes sense 🙂

Lennart Buit06:10:28

Hey there, I am trying to spec methods of protocols, but that doesn’t seem to be supported. Now this: https://groups.google.com/forum/#!topic/clojure/f068WTgakpk is suggesting to wrap protocol functions in ordinary functions and spec those. What I am wondering tho, the thread seems to suggest that is better anyway to wrap protocol functions, but those functions will never be more than a simple facade right? Why is this an accepted practice?

Wagk06:10:21

Hello, can anyone explain to me how clj performs module management?

Wagk06:10:47

I'm wondering specifically if it's possible for a namespace to not have a folder component (or what I think is a folder component)

andy.fingerhut06:10:16

Do you mean: Is it possible to create a Clojure namespace, but not create a file in a correspondingly named directory and file name in the file system that corresponds to it?

andy.fingerhut06:10:27

If so, the answer is yes, but not sure if you are asking that question.

Wagk08:10:28

@andy.fingerhut More along the lines of that (ns foo.bar) implies a folder structure of foo/bar.cljs, so is it possible to have a (ns foo) with the file foo.cljs.

Wagk08:10:59

though with some fiddling I've figured out that early errors that caused this confusion came from a typo elsewhere

Wagk08:10:15

and that it's possible

dev4openid09:10:13

I am working through clojurekoans however am stumped at this problem (= 25 ( (fn [n] (* n n))))

Lennart Buit09:10:32

You can format code by using backticks (` )

Lennart Buit09:10:01

Like so: (fn [a] (+ a 6))

dev4openid09:10:31

I have an answer but I cannot get head around solution

dev4openid09:10:47

being fn [n f] (f n)) 5

dev4openid09:10:00

It does not make sense. as the solution does not have sufficient arguments in the first place never mind the fact it responds by calling the second function with an argument.

dev4openid09:10:25

Any ideas? I have 2 days on this problem and cannot find and explanation anywhere

dev4openid09:10:44

...and in English....I have spent 2 days....

dev4openid09:10:49

The complaint is the lack of arguments so it fails

dev4openid09:10:16

Why does this work?

dev4openid09:10:49

apologies noobie

Space Guy09:10:49

OK, a solution I'm getting is:

;; at the top, next to 'square'
(defn use-5-on-this [f]
  (f 5))
  
"Higher-order functions take function arguments"
(= 25 ( use-5-on-this
   (fn [n] (* n n))))
Did either of your revious solutions work? I get an exception when I try those

dev4openid09:10:19

the solution at the bottom of the snippet works

dev4openid09:10:38

the real states true

Space Guy09:10:19

OK, didn't see the rest of that snippet. Will check

dev4openid09:10:50

but why would it work - if there is only 1 argument?

dev4openid09:10:05

It seems that it is cheating and getting a partial result?

Space Guy09:10:08

The second argument is the (fn [n] (* n n)) (the newline doesn't matter)

pradyumna09:10:22

yep that's correct

pradyumna09:10:57

(zxc 5 nil) doesn't return a funciton, rather it is a direct call

Space Guy09:10:15

((fn [a b] (b a)) 5 (fn [n] (* n n)))

( (fn [n] (* n n)) 5 )

(* 5 5)

25

dev4openid09:10:43

ok - got it - the second function is an argument!!!

jaihindhreddy-duplicate11:10:59

You can make it even more concise using the reader syntax, like so: (= 25 (#(% 5) (fn [n] (* n n))))

dev4openid10:10:37

Of course - me being blinkered and all!

Michael Fiano10:10:26

If I have a byte array, what is a good way to get the sequence of bytes from the start and up to but not including a given other sequence of bytes?

Michael Fiano10:10:12

I won't know the length, and I'd like to do this without interop

pradyumna11:10:47

@mfiano not optimal though, you can try something like this. it will return the full sequence if the terminating sequence not found.

noisesmith17:10:42

just FYI this would work without the inner let block (let [a [..] t [..] na (count a) nt (count t)] ...)

jaihindhreddy-duplicate12:10:24

@mfiano how about (let [idx (clojure.string/index-of s p)] (if (nil? idx) s (subs s 0 idx))) Of course the string s and pattern p would have to be strings. Are you looking for efficiency?

Pontus14:10:28

Hi all. Do you create intermediate collections when using the threading macros in clojurescript? -> ->>

tavistock15:10:12

yea it just turns threaded code into ‘normal’ code, (->> xs (map #(* 3 %)) (filter odd?)) becomes (filter odd? (map #(* 3 %) xs)), you can look at this with macroexpand. If the ‘normal’ code uses intermediate collections then yes it will use intermediate collections, the answer to your question is most of the time yes.

chrisulloa15:10:01

You could also use transducers in the above case to avoid intermediate aggregates.

Pontus15:10:45

Thanks for the answers, yes I'm considering using transducers but just wasn't sure if the threaded variant was skipping intermediate collections already.

tavistock15:10:56

if you use tranducers then the answer is no, most the time I use threading I dont use transducers. When in doubt macroexpand (if your in the repl you can just paste you code with ' in front (macroexpand '(->> xs (map #(* 3 %)) (filter odd?)))) and then reason about the code.

👍 4
Sturm21:10:14

I'm seeing namespaced keywords printing out like this at my REPL. Is that a "feature" or is something configured wrong? #:x{:foo 1} when I type {:x/foo 1}

Sturm21:10:42

Oh, it's some kind of "extract the common namespace" feature by the looks. {:x/foo 1 :y/foo 2} doesn't do the same thing.

andy.fingerhut21:10:02

This is intentional new behavior in Clojure 1.9

dev4openid22:10:06

Any ideas appreciated

hiredman22:10:44

((println .... is pretty much always a bug

hiredman22:10:47

(a b c) is calling a with the arguments b and c. ((println 1 2) 3) is calling the result of println (which is always nil) on the argument 3

dev4openid22:10:22

OK, so I removed at the println and still get the same result being ....NullPointerException user$zxc.invokeStatic (:2)

hiredman22:10:52

you like still left the outer parens

andy.fingerhut22:10:21

It wasn't the println alone that was the problem, it was that you had an unnecessary set of parens around the body of your defn

andy.fingerhut22:10:07

without that unnecessary set of parens, the println would be ok

dev4openid22:10:33

but I have removed the printlns and still get the same error

andy.fingerhut22:10:42

unnecessary is too mild a term -- harmfully unnecessary extra parens.

andy.fingerhut22:10:55

parens is short for parentheses ()

hiredman22:10:17

user=> ((println) (loop []))

NullPointerException   user/eval11 (NO_SOURCE_FILE:2)
user=> ((loop []))
NullPointerException   user/eval149 (NO_SOURCE_FILE:3)
user=> 

andy.fingerhut22:10:34

You had something like this (defn zxc [a b] ((println a b "*****") ...)). We are recommending you change that to (defn zxc [a b] (println a b "******") ...)

andy.fingerhut22:10:57

Please read my previous message very carefully

hiredman22:10:19

you are now trying to call the result of the loop as a function

andy.fingerhut22:10:37

parentheses are significant in Clojure and other Lisp languages. You can't remove them when they are needed, and you can't add the when they are unneeded.

hiredman22:10:21

(...) means function application so ((loop [...] ...)) means evaluate the loop then invoke the result as a function, and your loop evaluates to nil

andy.fingerhut22:10:57

In many programming languages, and mathematics, you can add redundant parentheses without changing the meaning. That isn't true in Clojure.

dev4openid22:10:45

ok so the problem is thew loop eventually resolves to a nit as there is nothing to process, whereas you infer it should present the answer as the result? Correct?

hiredman22:10:13

the problem is the loop returns nil, and you are invoking the result of the loop as a function

hiredman22:10:29

so you are trying to call nil as a function

andy.fingerhut22:10:09

(nil), or (any-expression-here-that-returns-nil), causes an exception

andy.fingerhut22:10:58

nil is fine. (nil) is an error

andy.fingerhut22:10:04

when it is code at least

hiredman22:10:16

you had (a b) where a was the println expression and b was the loop expression, because println returns nil, you had (nil b) which is an error, you removed the println and left yourself with (b) where b is still the loop expression, and that loop expression (just like the println) returns nil, so you have (nil) which is still an error

jimbob22:10:20

whats the best way to create a memoized or cached variable dependent on 2 arguments? I want to make a client thats dependent on 2 values passed in from a config and i want to keep referencing it later on. should i use:

(def client
  (memoize
    (fn [client-config]
but at the same time the fconfig will not change for this during system run time so instead cant i just cache the value? but it seems overkill to use cache. defonce pass in a function?

jimbob22:10:40

can comment in thread so we dont clutter the channel

andy.fingerhut22:10:30

Are you saying that if you memoize it, then the cache will never contain more than 1 set of arguments, because those are the only arguments the function will ever be called with during a run of your program?

jimbob22:10:37

delay makes sense as well

jimbob22:10:49

to cache the results of this function at the only place that calls it.. aka client start-up

andy.fingerhut22:10:45

Yeah, given your answer of "yes" to my question, delay make sense.

andy.fingerhut22:10:25

memoize should be able to handle functions with any number of arguments, but they must be arguments for memoize to "know" about them.

andy.fingerhut22:10:10

and must be values that return true when you call clojure.core/= to compare them, if you want the advantages of memoization, so mutable Java objects are a bit out of scope for memoization.

👍 4
hiredman22:10:03

if you know js you have something like function () {return null;}()()

hiredman22:10:29

(not exactly that because not everything in js is an expression)

dev4openid22:10:47

Excuse my noobieness ....loop [x a y b] - I understood that the use of a b here is to assign values to X and Y so I am confused here

dev4openid22:10:14

So on the recur x and y would pick up the values

hiredman22:10:24

sorry, my a b were meant as meta syntactic variables and not related to the a and b in your program

hiredman22:10:42

you had an expression like ((println ...) (loop ...)) which is a list containing two items (println ...) and (loop ...)

hiredman22:10:49

a list means function application

hiredman22:10:11

so you were apply the result of (println ...) to the result of (loop ...)

hiredman22:10:41

the result of (println ...) is always nil, and that is an error

hiredman22:10:03

you removed (println ...) from the expression and were left with ((loop ...))

hiredman22:10:33

((loop ...)) is a list containing a single item (loop ...)

hiredman22:10:57

so you are calling the result of (loop ...) as a function with no arguments

dev4openid22:10:57

I get the loop is a "single" function

hiredman22:10:06

no, you don't

hiredman22:10:17

loop is not a single function

andy.fingerhut22:10:18

line 3 has a harmfully unnecessary left parentheses that should be deleted, as well as the one it matches.

andy.fingerhut22:10:40

It changes the meaning of what your program does in a way you do not want.

hiredman22:10:10

(W) means invoke W as a function, whatever it is, for you, in your program, W is the expression that looks like (loop ...)

hiredman22:10:32

so the loop runs, and the result of the loop is nil, and then you invoke that nil

hiredman22:10:59

your ifs also are not correct

hiredman22:10:19

I'd suggest using an editor that at least highlights matching parens, and even better one that supports some kind of structural editing (like paredit)

dev4openid22:10:05

I am using rainbow paren and paredit in ms code

hiredman22:10:20

(the incorrect ifs appear to have been introduced when deleting the println)

hiredman22:10:00

the second if isn't actually nested in the first (as I assume you meant it to be)

hiredman22:10:11

with paredit in emacs, either of two structural editing commands would have removed the issue with the first version of the program, raise-sexp on the loop expression would have deleted the surrounding ((println ...) ....), and splice-sexp would have removed just the surrounding function call

dev4openid22:10:44

Hi not using emacs ms VScode, nevertheless I will have to think about what you have stated and come back with a better solution 😉

dev4openid22:10:54

to both of you

andy.fingerhut22:10:47

no worries. As at least one alternative, I have successfully edited a lot of Clojure code without paredit, too, and it isn't everyone's cup of tea. You don't need them to be effective in a Lisp. You do need to know where the parens are needed, and where they are not.

Sy Borg23:10:20

sorry for a stupid question - how to run leiningen 2.8.1 with clojure 1.9.0? it still uses 1.8.0

Alex Miller (Clojure team)23:10:39

change the version in your project.clj

Sy Borg23:10:52

I mean repl

Alex Miller (Clojure team)23:10:31

the repl’s classpath and clojure version are based on the dependencies in your project.clj

Sy Borg23:10:20

I created project.clj in lein home dir, it worked. before there was only profile.clj. thanks, Alex

CyberSapiens9723:10:31

i don't get, why the default version is 1.8.0 ?

hiredman23:10:30

because if you are doing something where you get the default version you are doing it wrong

andy.fingerhut23:10:27

Because the Leiningen developers have not chosen (yet) to release another version of Leiningen that defaults to Clojure 1.9. Perhaps there have not been significant changes to Leiningen since then to justify a new release.

hiredman23:10:11

lein is a build tool, but because clojure didn't ship with a very friendly launcher people pushed to shoe horn in the ability to run lein repl without a project.clj, but it was always a silly feature, lein is a build tool and you should run it in some project context with a project.clj that defines the version of clojure you use

seancorfield23:10:41

The latest version of Leiningen is 2.8.1 -- when that was released, Clojure was at clojure-1.9.0-beta3 so Leiningen defaults to 1.8.0 when used outside a project. I am a bit shocked there hasn't been a release of Leiningen since Clojure 1.9 dropped on December 8, 2017!