Fork me on GitHub
#beginners
<
2020-09-19
>
oxalorg13: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

✔️ 1
oxalorg13: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

oxalorg13: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)

oxalorg13: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))

1
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

sove16: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.

👍 1
lassemaatta16:09:13

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

sove17:09:24

binary h4x, very l33t.

oxalorg19: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: #{:load} at: [:ns-clauses :load :clause] spec: :clojure.core.specs.alpha/ns-load
: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))

🙏 1
seancorfield15:09:50

Welcome @cp4n! Saw your post on Reddit about learning Clojure!

🙂 1
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

you can use reader conditionals for that: https://clojure.org/guides/reader_conditionals

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}
         [:head
          [:meta {:name :viewport
                  :content "width=device-width, initial-scale=1.0"}]
          [:link {:href "/bootstrap/css/bootstrap.min.css"
                  :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

👍 1
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.

👍 1
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.

👍 1
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.