Fork me on GitHub
#beginners
<
2022-06-11
>
Mihai Chirculescu09:06:16

Is there a way to access a hashmap while building it without using let? Eg: I want to replace this from below with the actual hashmap I'm building. (for [x (range 2) y (range 3)] {[x y] (if (or (= x 0) (= y 0)) 1 (+ (get this [(- x 1) y]) (get this [x (- y 1)])))})

Mihai Chirculescu10:06:13

Actually it also doesn't work with let as I expected (see code below). I suppose it doesn't work because the hashmap is not formed yet. So I guess first I'll have to create the hashmap keys with some dummy value and then go through it and set the values. In which case I guess I'll use a vector instead of hashmap. Is there really no other way to do this in one step only instead of 2? (for [x (range 2) y (range 3)] (let [h {[x y] (if (or (= x 0) (= y 0)) 1 (+ (get h [(- x 1) y]) (get h [x (- y 1)])))} ] (get h [1, 2])))

Martin Půda10:06:56

What is expected output? Is that a Pascal's triangle?

Mihai Chirculescu10:06:27

It's not a Pascal triangle. It's the solution for this problem ( https://leetcode.com/problems/unique-paths/ ) using some random hardcoded input. The expected output is 3.

Mihai Chirculescu15:06:55

After some googling, apparently the Clojure approach to this kind of problems is to solve them purely recursively and then apply memoization: https://github.com/m0wfo/clojure-dynamic/blob/4e5730eabb809fee95d81cce17e61dbfaf93869d/src/main/clojure/com/mowforth/dynamic/fib.clj

Mihai Chirculescu15:06:12

If you guys have some other insights, please share.

Mihai Chirculescu15:06:07

So, basically, for my exact problem (although I'm interested in general on the approach of Clojure to dynamic programming solutions) would be something like this: https://github.com/Mihaiii/LearningClojure/blob/main/solutions/Unique%20Paths.clj

didibus23:06:44

Just use a loop/recur instead

didibus23:06:48

(let [r1 (range 2)
      r2 (range 3)]
  (loop [m {} x (first r1) xs (next r1)]
    (if x
      (recur (loop [m m y (first r2) ys (next r2)]
               (if y
                 (recur (assoc m
                               [x y]
                               (if (or (= x 0) (= y 0))
                                 1
                                 (+ (get m [(- x 1) y]) (get m [x (- y 1)]))))
                        (first ys)
                        (next ys))
                 m))
             (first xs)
             (next xs))
      m)))

;; => {[0 0] 1, [0 1] 1, [0 2] 1, [1 0] 1, [1 1] 2, [1 2] 3}

didibus00:06:05

Or use a mutable map instead:

(let [mm (java.util.HashMap.)]
  (doseq [x (range 2)
          y (range 3)]
    (.put mm
          [x y]
          (if (or (= x 0) (= y 0))
            1
            (+ (.get mm [(- x 1) y])
               (.get mm [x (- y 1)])))))
  mm)

;; => {[1 0] 1, [0 0] 1, [1 1] 2, [0 1] 1, [1 2] 3, [0 2] 1}

didibus00:06:53

Or with memoization:

(def compute-val-memo
  (memoize
   (fn [x y]
     (if (or (= x 0) (= y 0))
       1
       (+ (compute-val-memo (- x 1) y)
          (compute-val-memo x (- y 1)))))))
(into {}
      (for [x (range 2) y (range 3)]
        [[x y] (compute-val-memo x y)]))

;; => {[0 0] 1, [0 1] 1, [0 2] 1, [1 0] 1, [1 1] 2, [1 2] 3}

Mihai Chirculescu16:06:12

Thank you very much for your help and for providing multiple alternatives! I went through all of them and I learnt a little bit from them all. Your second solution is the bottom-up approach I was looking for. Thanks again!

David Stellini13:06:11

Hey 👋 Sorry for this super basic question - I'm making an HTTP request and trying to parse the response before returning it:

(defn weather-for-city
  [city]
  (->> (client/get ""
                 {:query-params {:q city :units "metric" :APPID "my-api-key"}
                  :headers {:Accept "application/json"}
                  :as :json})
        ({:degrees "test" :name city})))
I want to read "degrees" from the response (not sure about how to do the async / callback bit) and return it

pavlosmelissinos14:06:55

Not sure how idiomatic your workflow is but I'll assume you know how to evaluate expressions directly from your editor. The first step is that you need to get the :body of the response. However, you're sending the request as a json and I don't remember how clj-http works behind the scenes (you might have to parse the json string). Do you know what the response looks like? You are having difficulties because an HTTP call is involved. My advice would be to move it out of the equation until you understand what it returns: 1. put the HTTP call into a def and call it -for a specific city- (def is public so you will be able to see the result from your REPL) 2. if you have the response bound to a name you can examine it and transform it however you want (this is obviously bad for actual production code, but very useful during development).

(def response (client/get ""
                 {:query-params {:q city :units "metric" :APPID "my-api-key"}
                  :headers {:Accept "application/json"}
                  :as :json}))

David Stellini14:06:18

client/get is synchronous if you don't pass async: true right?

David Stellini14:06:51

I was able to place a breakpoint to understand what it returns

David Stellini14:06:06

I want to read this from the response and return it

{:degrees (-> (first (-> :response :body :weather)) :main) :name :city})

pavlosmelissinos14:06:38

Maybe this then

(defn weather-for-city
  [city]
  (let [{:keys [body]} (client/get ""
                                   {:query-params {:q city :units "metric" :APPID "my-api-key"}
                                    :headers {:Accept "application/json"}
                                    :as :json})]
    {:degrees (-> body :weather first :main)
     :name    (:city body)}))

pavlosmelissinos14:06:24

But this assumes that the response looks like this:

{:body {:weather [{:main <degrees>, ...} ...]
        :city <city>,
        ...},
 ...}

David Stellini14:06:17

Got it, thanks to you! ❤️ Using this:

(defn weather-for-city
  [city]
  (let [{:keys [body]} (client/get ""
                                   {:query-params {:q city :units "metric" :APPID "my-api-key"}
                                    :headers {:Accept "application/json"}
                                    :as :json})]
   {:degrees (-> (first (-> body :weather)) :main) :name :city}
))

pavlosmelissinos14:06:30

Awesome! Btw you don't need the -> twice, (-> (first (-> body :weather)) :main) is the same as (-> body :weather first :main), which is arguably a tad simpler 🙂

David Stellini14:06:30

I was wondering about that, it looked too long to be right

pavlosmelissinos14:06:59

If you're also wondering about {:keys body]}, take a look at the https://clojure.org/guides/destructuring guide. Very handy and you can Ctrl-f :keys if you're in a hurry (but destructuring is widely used so don't skip it 😉).

David Stellini14:06:37

thank you so much!

David Stellini14:06:03

Something as a sidenote, do clojure devs generally use println and dump vars for debugging, or is there a good debugging experience like chrome devtools?

pavlosmelissinos14:06:55

In JVM Clojure I don't really use println, but I dump vars (`def`) all the time and I think it's a pretty standard way of debugging. There are other ways of course (you might want to take a look at clojure taps, reveal and portal). In Clojurescript it's different because there's decent integration with chrome devtools, e.g.: https://github.com/binaryage/cljs-devtools and for reframe in particular, reframe-10x is supposed to be very powerful (haven't used it yet but it's on my radar). Clojure in general doesn't need step-by-step debugging as much as other languages, due to its immutability. That has been my experience at least, compared to Python. I've never had the need to know the full program state. With that said, there's been talk about a debugging experience that as soon as you get an exception, allows you to fix the error and/or code the missing parts and continue execution (inspired by https://pharo.org//https://gtoolkit.com/). I definitely wouldn't be opposed to something like that in Clojure 😄.

David Stellini15:06:07

I can think of a bunch of sitiuations where you'd need to know the state - hitting a db or hitting an API and see what it gave you should be some of them no?

pavlosmelissinos15:06:24

Right but as soon as you run that and you have the result into your program it's no longer a stateful thing. I mean it won't be affected by your actions, it's not like, e.g. an object of a class where you can call a method and boom the old version is lost. Why do you need to monitor it? Capture it once as data and transform it. In my experience the value of step-by-step debugging is that you can jump up and down the stack but you don't really need that with Clojure because your data is the same at any stack level (unless you're doing recursion but even then you can still reason about the data 🙂). The web is a different beast but you can apply the same principles there as well.

didibus01:06:37

I use cider debugger, I think Calva support the same, and with Cursive there is a Java style debugger that supports Clojure

didibus01:06:30

But ya, sometimes people do tracing debugging instead, either with println or with a macro that auto-prints it all. There's also a pretty good general tracing debugger for Clojure here: https://github.com/jpmonettas/flow-storm-debugger

David Stellini13:06:29

I'm using this one:

[clj-http.client :as client]