Fork me on GitHub
#beginners
<
2020-09-19
>
oxalorg (Mitesh)13:09:24

Hi everyone. I want to know what would be the idiomatic way to do something like this in clojure: To find the first power (exponent) of 2 which is greater than a give number. (So for 10 it is 2^4, for 5 it is 2^3) In python my first instinct would be to do something like this:

``````def find_closest_exponent(num, base=2):
power = 0
while num > 1:
num = num / base
power += 1
return power``````
In clojure my first instinct was something like this:
``````(defn find-closest-exponent [num base]
(loop [power 0 acc num]
(if (< acc 1) power
(recur (inc power) (/ acc base)))))``````
This is good enough and it works, I'm just trying to expand my thinking if there are other ways you would use to solve something like this?

james13:09:37

Off-topic: You can do this with the `bit_length` method in Python.

james13:09:44

(Sorry, don't know if there's an equivalent in Clojure, but Common Lisp has it, so not impossible, I guess.)

jsn13:09:12

I think your solution is fine

jsn13:09:02

I would probably grow acc from 1 and compare it to num instead, but that's just my personal taste, perhaps

✔️ 3
oxalorg (Mitesh)13:09:26

Thanks I tried porting the python code as-is using `(while)` in clojure, but that meant I had to mutate the variables which didn't feel right

oxalorg (Mitesh)13:09:38

(also off topic, we both have a Rurourni Kenshin profile picture haha)

jsn13:09:45

huh! 🙂 yeah, but I didn't even watch it, I think (even though I have this avatar pic for like 20 years or something)

oxalorg (Mitesh)13:09:56

Ah I see! It's wonderful though if you ever get the time to watch. 🙂

caio14:09:04

a really memory inefficient solution:

``````(defn closest-exponent [num base]
(->> (iterate #(/ % base) num)
(take-while #(> % 1))
count))``````

3
jsn14:09:55

@U0GRKUGLQ well, I think its memory requirements are O(1), so I wouldn't call it memory inefficient 🙂

caio14:09:54

it's actually `O(logbase(num))` since using `take-while` will keep the sequence in-memory before counting

caio14:09:29

extracting this counting logic:

``````(defn count-while [pred coll]
(or (->> (map-indexed list coll)
(drop-while #(pred (second %)))
ffirst)
(count coll)))

(defn closest-exponent [num base]
(->> (iterate #(/ % base) num)
(count-while #(> % 1))))``````
this would be `o(1)` memory

jsn14:09:13

I don't think so, there's no reason for `take-while` not to return a `lazy-seq` , and `count` doesn't keep head

jsn14:09:27

So it's O(1), I think

caio14:09:59

hmm. that makes sense

sova-soars-the-sora16:09:15

looks like the perfect use case for memoize

lassemaatta16:09:12

if you're willing to do a bit of java interop and bit shifting, then the `Long` and `Integer` classes have some methods (e.g. `highestOneBit`) which might allow you to do this in constant time 🙂 I don't have a repl handy so I can't check whether that function does what I think it does.

👍 3
lassemaatta16:09:13

perhaps `(fn [i] (* 2 (Long/highestOneBit i)))` is enough

sova-soars-the-sora17:09:24

binary h4x, very l33t.

oxalorg (Mitesh)19:09:28

Thanks everyone! Thanks caio, although I did not understand what was happening with your code at first, after hours of http://clojuredocs.org and giving it some thought now it looks straightforward and wonderful!

amirali13:09:47

Hi folks When i want to add this block to ns i get following error. Can u figure out whats happening here?

``````(ns something
(:import
(java.util.concurrent.Callable)
(java.util.concurrent.ExecutorService)
(java.util.concurrent Executors))) ``````
``````Syntax error macroexpanding clojure.core/ns at (core.clj:1:1).
() - failed: Insufficient input at: [:ns-clauses :import :classes :package-list :classes] spec: :clojure.core.specs.alpha/package-list
(java.util.concurrent.Callable) - failed: simple-symbol? at: [:ns-clauses :import :classes :class] spec: :clojure.core.specs.alpha/import-list
:import - failed: #{:refer-clojure} at: [:ns-clauses :refer-clojure :clause] spec: :clojure.core.specs.alpha/ns-refer-clojure
:import - failed: #{:require} at: [:ns-clauses :require :clause] spec: :clojure.core.specs.alpha/ns-require
:import - failed: #{:use} at: [:ns-clauses :use :clause] spec: :clojure.core.specs.alpha/ns-use
:import - failed: #{:refer} at: [:ns-clauses :refer :clause] spec: :clojure.core.specs.alpha/ns-refer
:import - failed: #{:gen-class} at: [:ns-clauses :gen-class :clause] spec: :clojure.core.specs.alpha/ns-gen-class``````

pithyless13:09:51

``(:import (java.util.concurrent Callable ExecutorService Executors))``

🙏 3
seancorfield15:09:50

🙂 3
cp4n15:09:36

Thanks @seancorfield, really excited to learn it. There was a lot of useful advice in the responses!

Michael W20:09:51

Is it better to use `(Long/parseLong "10")`or `(long (bigdec "10"))` to coerce a string to a long?

jsn21:09:13

is there a reason why there's a `bigdec` constructor taking a string but there is no `long` constructor taking a string?

Michael W21:09:43

I can't seem to use Long/parseLong as a function in an update: `(update {:a 1 :b "2"} :b Long/parseLong)` this fails: Unable to find static field: parseLong in class java.lang.Long

caio21:09:12

you have to wrap it in a fn

jsn21:09:15

@michael819 it's because it's not quite a function, it's a static java method; try using `#(Long/parseLong %)` instead?

Michael W21:09:38

Hmmm it's not compatible with cljs either so it causes a build warning... Need to figure something out that will work for both. Thanks for the help, it's working in clj but not cljs.

schmee21:09:14

schmee21:09:21

to have one expression for clj and one for cljs

jsn21:09:50

well, I don't think javascript even has longs

Michael W21:09:36

I guess I need an integer not a long, but same thing, works in Java, not in JS

jsn21:09:16

I don't think js actually has integers, either 🙂 anyway, `js/parseInt` should work in clojurescript, I suppose

jsn21:09:09

`#(#?(:clj Long/parseLong :cljs js/parseInt) %)` works in my tests

jsn21:09:53

(man, I have serious problems with my cut-n-paste skills :/)

Michael W21:09:53

Yeah that removed the build warnings, I don't even think my code will get ran in cljs, it's a resolver for fulcro, but rather not have the warnings.

Michael W21:09:04

Thanks again.

Josef Richter21:09:25

hi, I have this simple hiccup page that receives a map correctly, but for some reason accessing keys of that map returns `nil` - can anybody please tell me why? I expect this to be some obvious noob mistake… see FIXME below:

``````(ns webdev.item.show
(:require [hiccup.page :refer [html5]]
[hiccup.core :refer [html h]]))

(defn item-page [item]
(println item) ;; => ({:id #uuid "84a8c493-717d-41be-ba3d-52805b495524", :name one, :description one, :checked false, :date_created #inst "2020-09-18T11:32:18.416622000-00:00"})
(println (:name item)) ;; => nil FIXME why is this nil?
(html5 {:lang :en}
[:meta {:name :viewport
:content "width=device-width, initial-scale=1.0"}]
:rel :stylesheet}]]
[:body
[:div.container
[:h2 "Item detail"]
[:div.row
(h (:name item))]
[:div.row
(h (:description item))]]]))``````

andy.fingerhut21:09:23

The output from `(printn item)` that you show has parens around it, so it is probably either a list or some other kind of sequence of maps, not just a map.

andy.fingerhut21:09:12

``````user=> (def x1 '({:a 1 :b 2}))
#'user/x1
user=> (:a x1)
nil
user=> (def m1 '{:a 1 :b 2})
#'user/m1
user=> (:a m1)
1
user=> (:a (first x1))
1``````

👍 3
Josef Richter21:09:06

bingo!! I’m getting a list with a single map in it. understood, thank you!

Josef Richter21:09:47

ok fixed. maybe the question that comes to my mind - does jdbc/query always return a list?

seancorfield21:09:06

`clojure.java.jdbc/query`? Yes.

👍 3
seancorfield21:09:49

`get-by-id` returns a single hash map (representing a single row). `query` and `find-by-keys` return a "result set": a sequence of hash maps.

👍 3
seancorfield21:09:01

@josef.richter If you have questions about `clojure.java.jdbc` (or it's replacement, `next.jdbc`), the #sql channel is a good place to get detailed answered (and I'm more likely to see questions there because it's lower traffic.