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!

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

👍 1
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 [email protected]:/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!!

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

🙏 1
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``````

❤️ 1
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))]))))``````

❤️ 1
slk50020:01:00

wow that's much simpler!

hiredman20:01:57

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

👍 1

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`

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``````

❤️ 1
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 `if`s, so it actually doesn't matter.

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