Fork me on GitHub
#beginners
<
2023-01-27
>
noob.clj00:01:34

Hey all, running into a weird issue. So I am trying to write a method that’ll take a number as input and return the number which is smaller than current number and is a power of 2. Eg: enter 257 will return 256, enter 255 will return 128.

(def possible-values (map #(int (Math/pow 2 %)) (range)))

(defn get-just-smaller [value]
  (last (filter #(>= value %) possible-values)))

(get-just-smaller 5)
My code when I run first time throws IllegalArgumentException with ; Value out of range for int: 2.147483648E9 error. It works the second time I run in same REPL. If I remove casting to int program goes into an infinite loop 😞. As per my understanding filter and map both return lazy sequence but I guess what is happening is filter is simply trying to use possible-values as an unbounded array. Now one approach I can take is only to generate values less than value by converting possible-values to a function and looping by passing the target value. However, is there a way to make this code work?

hiredman00:01:59

filter doesn't know that the predicate you give it will never return true again

hiredman00:01:13

so an infinite sequence input to filter results in a sequence that in theory is finite, but never arrives at its end, so last can never know when it has arrived at the last element to return

hiredman00:01:50

you likely want something like take-while

seancorfield00:01:16

Or drop-while and first (instead of last)?

seancorfield00:01:23

(and a reversed condition)

Slacki ng00:01:32

your (range ) function is empty so it uses the defaults, this implies (range INF), also why without the loop it is an infinite loop

where start defaults to 0, step to 1, and end to
infinity.
https://clojuredocs.org/clojure.core/range the (int ) is hitting the upper bound memory allocation for Java's Integer
A constant holding the maximum value an int can have, 2^31-1.
https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html (range 31) works fine (range 32) gives your error user=> (def possible-values (map #(int (Math/pow 2 %)) (range 31))) #'user/possible-values user=> possible-values (1 2 4 8 16 32 64 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152 4194304 8388608 16777216 33554432 67108864 134217728 268435456 536870912 1073741824) user=> (def possible-values (map #(int (Math/pow 2 %)) (range 32))) #'user/possible-values user=> possible-values Error printing return value (IllegalArgumentException) at clojure.lang.RT/intCast (RT.java:1255). Value out of range for int: 2.147483648E9 it works the second time because instead of re running the (def possible-values ) it uses the values that were stored in possible-values until the crash which is equivalent to (range 31)

hiredman00:01:14

something to keep in mind is you are dealing with binary integers, so there may be a faster way to find the next smaller power of two using bit twiddling

hiredman00:01:41

there may even be a method on Long that returns such a thing

noob.clj00:01:03

Thanks @U0NCTKEV8 take-while worked. There is such a small wording difference in their documentation. Thanks @U04V70XH6 will try drop-while as well. @U03GLUMEQSF Thanks for responding, I already understood the part why it was failing, just curious if there is an alternative to filter which works.

skylize01:01:53

I don't know if this is a good solution, but it is definitely a solution:

(def pow2s (map #(int (Math/pow 2 %)) (range)))

(defn just-smaller-pow2 [value]
  (some (fn [[k v]]
          (when (> k value) v))
        (map (fn [k v] [k v])
             pow2s
             (cons 0 pow2s))))

(just-smaller-pow2 5)
; =>
4
The map creates a lazy seq of pairs of [pow2, 1-pow2-below-that], filling in 0 for the 1-lower of 1. I think nil instead of 0 would also be perfectly reasonable. Depends how you want to deal with inputs that are the correct type of int but are essentially invalid. Then use some to look for the first pow2 that is higher than the input, and return the corresponding 1-pow2-below-that.

Sam Ritchie05:01:37

(let [l2 (Math/log 2)]
  (defn log2 [x]
    (/ (Math/log x) l2)))

(defn your-fn [x]
  (long
   (Math/pow 2 (Math/floor (log2 x)))))

Sam Ritchie06:01:02

actually depending on how you want the actual powers of 2 to break…

(defn your-fn [x]
  (if (zero? x)
    x
    (Long/highestOneBit (dec x))))

(defn your-fn [x]
  (Long/highestOneBit x))

stantheman21:01:12

Ok using Sean's tip but with take-while...

metapredicate00:01:14

I am unsure if this is the best place to ask this but - has anyone checked out https://cleancoders.com/series/clean-code/functional-programming? Clean Coders really made me a better java dev - or it fast tracked the java road path - SOLID / Design Patterns / Good software habits / refactoring etc. I am new to clojure and 90% of the code I write at work is going to be clojure from now on.

noob.clj00:01:10

I haven’t tried that so don’t have answer to your exact question. But Exercism is pretty good along with book Programming Clojure. At this point I am able to read other people’s solutions on Exercism and can appreciate really elegant solutions 😛

metapredicate01:01:03

I haven't heard of Exercism before

metapredicate01:01:19

It doens't seem to be too featured. I will definitely do it though if its free

kennytilton01:01:36

lucky you! I hear Uncle Bob is a big Clojure fan. I am not familiar with Clean Coders. You might like Paul Graham's "On Lisp" when it comes to Lisp-y coding. Free PDF: http://www.paulgraham.com/onlisptext.html

noob.clj02:01:48

> It doens’t seem to be too featured. I will definitely do it though if its free Yeah but their CLI is free and their questions are fairly simple for beginners. They also have a VSCode plugin which helps you with test run and quick submission.

respatialized13:01:54

https://github.com/oxalorg/4ever-clojure There's also the venerable 4clojure series, which if you haven't already seen, is a good way to familiarize yourself with the standard library and its idioms.

Roxana Danger13:01:13

Hello team, I am just starting with Clojure and Calva and I am testing them with a very simple example. The structure of my project is: • time ◦ deps.edn • src ◦ time.clj The content of the files deps.edn and time.clj is as described in the section "Writing a program" at https://clojure.org/guides/deps_and_cli. I then execute the command: Calva: Start a project and connect, selected my directory and type deps.edn. The output in the terminal shows an error and I do not get connected to the REPL. Terminal output: clojure -Sdeps '{:deps {nrepl/nrepl {:mvn/version,"1.0.0"},cider/cider-nrepl {:mvn/version,"0.28.5"}}}' -M -m nrepl.cmdline --middleware "[cider.nrepl/cider-middleware]" Missing required argument for "-M ALIASES" Jack-in process exited. Status: 1 What am I missing here?

dpsutton13:01:33

I’m sorry you’re having issues. I don’t know how to use calva (but lots of people do, it’s very popular). I’m sure you can find some help by asking this question in #CBE668G4R where lots of people will be able to point out what’s going on

Roxana Danger13:01:06

Thank you very much... Doing it just now!

pez13:01:54

Here works fine, @U04LNEP9KC6, and of course you are also welcome to the #CBE668G4R channel!

🙏 2
Roxana Danger13:01:51

I can't get it working...

pez13:01:00

I'm trying to understand why this happens. Saw an issue on Github about it too this morning. Maybe that was you, I now realize.

Roxana Danger13:01:28

yes, it was me... 🙂

pez13:01:00

Is the deps.edn really in a subdirectory time?

Roxana Danger13:01:33

yes, it is... what I noticed is that the call to clojure doesn't starts with "java -jar '/root/.vscode-server/extensions/betterthantomorrow.calva-2.0.326/deps.clj.jar' -Sdeps '{:deps {nrepl/nrepl ..." as when I first used the fire up Getting Started

Roxana Danger13:01:23

it is like it's not linked to calva...

pez13:01:49

Astute observation! The Getting Started REPL assumes clojure is not installed.

pez13:01:48

deps.edn should be in the root. Check if that helps. If it does then we need to fix a better UI in Calva.

Roxana Danger13:01:35

it is in the root of the project...

pez13:01:35

What's the content of deps.edn?

Roxana Danger13:01:08

{:deps {clojure.java-time/clojure.java-time {:mvn/version "1.1.0"}}}

pez13:01:45

Cool. That should work. Let me try this and see what I get.

👍 2
Roxana Danger13:01:17

One complication is that I am testing on a devcontainer but the Getting Started worked perfectly...

pez13:01:29

I see. Maybe that could be a clue. I'll try both.

Roxana Danger13:01:29

Thank you very much!

pez13:01:05

Which version of clojure are you using?

clojure --version

Roxana Danger13:01:18

clojure --version Execution error (FileNotFoundException) at http://java.io.FileInputStream/open0 (FileInputStream.java:-2). --version (No such file or directory) Full report at: /tmp/clojure-12876054732791771324.edn root@fdc5986a266f:/workspaces/clojure# clojure Clojure 1.10.1 user=>

Roxana Danger13:01:19

1.10.1, but there was an error trying to get the version directly

pez13:01:34

That doesn't look like I expect it to.

pez13:01:19

Do you know where it is installed? If so you could try rename it so that Calva doesn't find it, and try if it works then.

Roxana Danger13:01:19

I can set the full project in a git repo if that helps...

Roxana Danger13:01:44

clojure is in bin: /usr/local/bin/clojure

pez13:01:48

With devcontainer and all? That would be great. I don't think it is about the devcontainer anymore, now that it seems it is the clojure executable that is funny. But a repo with your setup would be good anyway.

pez13:01:32

I now see that Calva has a setting for using old clojure versions. Search for ”Calva deprecated” in VS Code setting and enable Use Depecrated Alias Flag

pez13:01:29

Just to see if that works. The real fix is probably that you update clojure.

Roxana Danger13:01:32

Calva version: v2.0.327

Roxana Danger13:01:28

found the setting, and it worked!!

metal 2
pez13:01:38

Cool! Don't hesitate to reach out should Calva be acting up on you more.

Roxana Danger13:01:40

Thank you very much!! Really appreciated!! And... I will, I am super excited to work with clojure/calva!!

🙏 2
pez13:01:42

btw, a more standard structure of the project is: • deps.edn • src ◦ some_app_namespace ▪︎ time.clj Making the ns form in time.clj be:

(ns some-app-namespace.time)
It shouldn't matter, but I know there are tools that assume this structure.

Roxana Danger14:01:05

great!! thank you!

pez14:01:29

NB: It should be underscores separating the words in some_app_namespace in the directory name, and dashes in the ns form. ( Super easy mistake to do to use dashes in the filename. I actually wrote it wrong first and updated the comment...)

Roxana Danger14:01:53

Wow!! this is subtle... 🙂 thank you!

slk50019:01:01

Here's a nested data structure: (def data [ {:name "a" :children [ {:name "b" :children [ {:name "c" :children []}]}]}]) Wanted result: ;; search for "a" -> will return [{:name "a" :children []}] ;; search for "b" -> will return [{:name "a" :children [{:name "b" :children []}]}] ;; search for "c" -> will return [{:name "a" :children [{:name "b" :children [{:name "c" :children []}]}]}] It's like cutting branch. Any ideas?

Martin Půda19:01:22

What should be the result for search for "d"?

slk50019:01:34

nil empty list

Martin Půda19:01:18

Can one element have more children? Something like:

(def data
  [{:name "a" :children [{:name "b" :children [{:name "c" :children []}]}
                         {:name "d" :children []}]}])

Martin Půda19:01:20

First attempt:

(def data
  [{:name "a" :children [{:name "b" :children [{:name "c" :children []}]}
                         {:name "d" :children []}]}])

(defn in-tree? [data item]
  (.contains (tree-seq associative? identity data)
             [:name item]))

(defn search-name [{:keys [name children] :as data} searched-name]
  (if (= name searched-name)
    (assoc data :children [])
    (assoc data :children [(->> children
                                (filter #(in-tree? % searched-name))
                                (#(search-name (first %) searched-name)))])))

(defn search [data searched-name]
  (when (in-tree? data searched-name)
    (search-name data searched-name)))

(search (first data) "a")
=> {:name "a", :children []}
(search (first data) "b")
=> {:name "a", :children [{:name "b", :children []}]}
(search (first data) "c")
=> {:name "a", :children [{:name "b", :children [{:name "c", :children []}]}]}
(search (first data) "d")
=> {:name "a", :children [{:name "d", :children []}]}
(search (first data) "e")
=> nil

❤️ 2
Martin Půda20:01:07

Second attempt (example calls are the same):

(defn in-tree? [data item]
  (.contains (tree-seq associative? identity data)
             [:name item]))

(defn search [{:keys [name children] :as data} searched-name]
  (when (in-tree? data searched-name)
    (if (= name searched-name)
      (assoc data :children [])
      (assoc data :children [(into {} (mapcat #(search % searched-name) children))]))))

❤️ 2
slk50020:01:00

wow that's much simpler!

hiredman20:01:57

Something to keep in mind is using .contains is a linear search

👍 2
tschady13:02:01

depending on what else you’re trying to do, clojure.zip might be useful to edit and navigate the tree

slk50020:02:41

@U1Z392WMQ thank you for a hint 🙂 After playing around with the code I had lower my desired requirements. Now just want to check if tree of tags str/includes? substring. e.g Can I use tree-seq for this? For a real use case of this functionality see my site https://culturevein.com/tags So far I have this: (def data [ {:name "a" :children [ {:name "b" :children[]} {:name "c" :children [ {:name "d" :children []}]}]}]) (defn includes-in-tags-tree? [tags substr] (loop [tag tags] (if (and (:name tag) (clojure.string/includes? (:name tag) substr)) true (if-let [child (first (:children tag))] (recur child))))) (includes-in-tags-tree? (first data) "a") ;; => true (includes-in-tags-tree? (first data) "b") ;; => true (includes-in-tags-tree? (first data) "c") ;; => nil -> should be true (includes-in-tags-tree? (first data) "d") ;; => nil -> should be true

tschady21:02:21

it doesn’t look very idiomatic IMO, but another powerful option is https://github.com/redplanetlabs/specter

slk50021:02:05

I'm newbie in clojure 😉 thx for another hint I will check this out 😄

Martin Půda22:02:30

Something like this?

(defn includes-in-tags-tree? [tags substr]
  (->> (tree-seq associative? identity tags)
       (some #(and (map-entry? %)
                   (let [[k v] %]
                     (and (= k :name)
                          (str/includes? v substr)))))))

(includes-in-tags-tree? (first data) "a")                   ;; => true
(includes-in-tags-tree? (first data) "b")                   ;; => true
(includes-in-tags-tree? (first data) "c")                   ;; => true
(includes-in-tags-tree? (first data) "d")                   ;; => true

❤️ 2
slk50022:02:10

thank you @U01RL1YV4P7 you are very kind and helpful

slk50010:02:35

that's really elegant solution. But I'm a little surprised with 'and' condition (some #(and (map-entry? %) and (and (= k :name) I would use 'if'. Is this is the way to do it or personal preference ? @U01RL1YV4P7

Martin Půda11:02:44

So, you want to write something like (if ... true false)? You shouldn't do that- see https://github.com/bbatsov/clojure-style-guide#converting-something-to-boolean.

slk50012:02:09

I would write : (defn includes-in-tags-tree? [tags substr] (->> (tree-seq associative? identity tags) (some #(if (map-entry? %) (let [[k v] %] (if (= k :tag_name_lowercase) (str/includes? v substr)))))))

slk50012:02:13

hmm... I'm not sure this the right case 'Converting Something to Boolean'. I understand code this way. with 'and': 1. check - (map-entry? %) - if true proceed to next form, 2. check - can bind key & value to symbol k & v - if true then whole 'and' is true and will proceed further But I understand that '(let [[k v] %]' will be always true, it is always possible, if '(map-entry? %)' is true. So I would use just 'if' => '(if (map-entry? %)' then just bind symbols, don't need to check if CAN bind those value and return true if can.

Martin Půda12:02:01

That will work, I'm just more used to and and or (and some expects some predicate, and small predicates joined with and look more like predicate) ... and and and or are macros and expand to nested ifs, so it actually doesn't matter.

👍 2
rafd23:01:35

Trying to quiet some reflection warnings, how can I annotate the type of "an array of SomeClass"? (clojure has ^ints etc. for some base classes, and I can do ^SomeClass for a single item)

rafd23:01:23

Hideous, but it works 😛