Fork me on GitHub
#beginners
<
2024-05-23
>
Melanie02:05:47

Hi there, I would need some help to clean up a little bit that code I wrote with regard to https://projecteuler.net/problem=39... I can surely define few functions, but also wondering if I've been smart in the way I went about that problem. I got the right solution, it seems simple in the end, but doesn't read quite as simple as I'd like... actually any review/comment appreciated. I feel there might be a better way without a for... took me a while to figure out how to actually "incrementally build a map of list... (still learning!)

(defn problem-39 []
  (first (apply max-key second
                (map #(vector (first %) (count (second %)))
                     (let [my-map {}]
                       (apply (partial merge-with set/union my-map)
                              (for [a (range 1 1000)
                                    b (range 1 1000)
                                    c (range 1 1000)
                                    :when (and (= (* a a) (+ (* b b) (* c c))) ; could be its own function
                                               (<= (+ a b c) 1000))
                                    :let [sum (+ a b c)]]
                                {sum #{(sort (list a b c))}})))))))

Melanie02:05:47

user=> (time (problem-39))
"Elapsed time: 13560.903292 msecs"

Melanie02:05:43

might look cleaner with some ->> in there...

Melanie02:05:50

I feel it does

(defn problem-39 []
  (->> (let [my-map {}]
         (apply (partial merge-with set/union my-map)
                (for [a (range 1 1000)
                      b (range 1 1000)
                      c (range 1 1000)
                      :when (and (= (* a a) (+ (* b b) (* c c))) ; could be its own function
                                 (<= (+ a b c) 1000))
                      :let [sum (+ a b c)]]
                  {sum #{(sort (list a b c))}})))
       (map #(vector (first %) (count (second %))))
       (apply max-key second)
       (first)))

Bob B03:05:45

you could remove one of the for bindings and just compute the value for it, rather than checking all the possibilities

Melanie03:05:54

oh... dang of course. how I didn't think about it. TY so much Bob!!!

Melanie03:05:20

let me know if you see any other issue with the style or what I'm doing, I'm still a fairly early writer of code in clojure, even if I have been reading some for many years.

Bob B03:05:23

I think I might also bind b to (range a 1000), and that should make it such that none of the triples would be checked more than once, which would eliminate the need for sets and/or sorting. And then, frequencies is quite a useful function that could come in handy here, especially if the for just returns the sum (because if the triples don't overlap, then the values of a, b, and c aren't really needed any longer).

πŸ’œ 1
Melanie04:05:05

Ok that's where I'm at from your great advices:

(defn right-triangle? [a b p]
   (let [c (- p a b)]
     (= (* c c) (+ (* a a) (* b b)))))

 (defn problem-39 []
   (->> (frequencies
         (for [perimeter (range 12 1001)
               a (range 2 perimeter)
               b (range a (- perimeter a))
               :when (right-triangle? a b perimeter)]
           perimeter))
   (apply max-key second)
   (first)))

Melanie04:05:57

no question, much better, lot simpler.

Melanie04:05:29

also renaming sum to perimeter makes more sense >_< and iterating through the perimeter makes it much faster: 2s vs ~13

Melanie04:05:36

user=> (time (problem-39))
"Elapsed time: 2153.018042 msecs"
840

Melanie04:05:51

Thank you so very much for your help! I think I'm happy with that code now πŸ™‚

Melanie04:05:05

let me know if you see any improvement or such that I could be missing at this point.

Bob B04:05:35

I did something pretty similar:

(->> (for [a (range 1 1000)
           b (range a 1000)
           :let [c   (math/sqrt (+ (* a a) (* b b)))
                 sum (+ a b (int c))]
           :when (and (zero? (mod c 1)) ; is c a whole number? 
                   (<= sum 1000))]
       sum)
  frequencies
  (apply max-key second)
  first)

πŸ’œ 1
Melanie05:05:12

oh I like that method to test if c is a whole number. I'll remember that.

Sasha V. Bogdanov11:05:40

Hello everyone! How to pass :jvm-opts to uberjar? Is it possible?

Alex Miller (Clojure team)11:05:58

If you mean like embedding them, that’s not possible

silian22:05:47

x-post from Ring channel

Melanie23:05:12

Hi! doing a little bit of the http://exercism.org exercises. The goal of the exercise I'm working on is transforming a map like this:

{1 ["APPLE" "ARTICHOKE"], 2 ["BOAT" "BALLERINA"]}
into
{"apple" 1 "artichoke" 1 "boat" 2 "ballerina" 2}
here in the 🧡 what I came up with as a solution. Any comments/ideas/more idiomatic way to write this would be appreciated.

Melanie23:05:28

(defn transform [source] ;; <- arglist goes here
  (reduce-kv 
   (fn [m k v]
     (apply merge 
      (for [val v]
        (assoc m (str/lower-case val) k)))) {} source))

Melanie23:05:55

:woman-shrugging: took me more time than I wanted to come up with a solution (like 45min/1h) and a lot of try and fail in the repl... still loving so much this language :smiling_face_with_3_hearts:

jpmonettas23:05:00

here is another solution, maybe a little bit more incremental to build

(defn transform [source]
  (let [m (->> source
               (mapv (fn [[n v]] (zipmap v (repeat n))))
               (reduce merge))]
    (update-keys m String/.toLowerCase)))

jpmonettas23:05:09

it is using String/.toLowerCase which needs Clojure 1.12, but you can replace it with your str/lower-case

jpmonettas23:05:20

with more incremental I mean you can start with :

(->> {1 ["APPLE" "ARTICHOKE"], 2 ["BOAT" "BALLERINA"]}
     (mapv (fn [[n v]] (zipmap v (repeat n)))))

[{"APPLE" 1, "ARTICHOKE" 1} {"BOAT" 2, "BALLERINA" 2}]

Melanie23:05:22

Oh I didn't know about update-keys... nice one. Curious on when to use String/.toLowerCase vs str/lower-case in general I'm not yet super familiar with zipmap, gonna read the doc now.

jpmonettas23:05:53

yeah, there is update-keys and update-vals, pretty useful

Melanie23:05:55

oh! I was looking for something like that earlier. (zipmap)... cool

jpmonettas23:05:51

regarding the lowerCase, since strings in Clojure are java.lang.String you can call the .toLowerCase method on them, without any extra ns require

Melanie23:05:53

so create a map from a collection of keys and collection of values with the lowest size of both.

Melanie23:05:04

I see that makes sense.

Melanie23:05:40

Thank you so very much it's super helpful to see other solutions. Do you think anything was wrong or not readable in my solution? I'm really trying to be good at writing understandable clojure.

Melanie23:05:19

I like your solution... It's probably what I wanted to do at first, but didn't find another solution that what I did.

Melanie23:05:45

I really like the fact you've used threading.

jpmonettas23:05:40

Do you think anything was wrong or not readable in my solution? I'm really trying to be good at writing understandable clojure.I don't think there is anything wrong. Going the reduce way you can also do :

(defn transform [source]
  (reduce-kv 
   (fn [m k v]
     (reduce (fn [mm val]
               (assoc mm (str/lower-case val) k))
             m
             v))
   {}
   source))

jpmonettas23:05:03

it think that is also a common pattern, where the inner reduce starts with the "accumulator so far"

πŸ’œ 1
thanks2 1
Nazral00:05:35

using a transducer

(let [data {1 ["APPLE" "ARTICHOKE"], 2 ["BOAT" "BALLERINA"]}]
  (into {} (mapcat (fn [[k vs]] (map (fn [v] [(str/lower-case v) k]) vs))) data))

Melanie00:05:00

Yeah transducers are still beyond my current level

Melanie00:05:46

It does look super clean that way tho...

Melanie00:05:25

ok so you're basically composing two map (one being a mapcat)

Melanie00:05:00

I need to get to the level where this would be natural to me. I don't think I could have come up with that one.

Nazral00:05:20

(let [data {1 ["APPLE" "ARTICHOKE"], 2 ["BOAT" "BALLERINA"]}
      xtrans (mapcat
              (fn [[k vs]]
                (map (fn [v] [(str/lower-case v) k]) vs)))]
  (into {} xtrans data))
more legible

Nazral00:05:46

it's still not natural to me πŸ˜„ just figured it would be a good way to do it there that wasn't shown yet

Melanie00:05:17

I think I start getting transducer... I understand it's about composing the transformations... But I haven't practice or try it yet.

Melanie00:05:45

Like most functions without the collection parameter returns a transducers, so we can compose them, and only pass the collection in the end... Is that the idea?

πŸ‘ 1
Melanie00:05:03

I did watch the various video about it... but it still a bit fuzzy to me.

Melanie00:05:35

it's good to see one example on an exercise I tried to solve. Thank you so very much Nazral!

πŸ™ 1
john00:05:28

That's only one transducer there though, not being composed. You could probably make a very similar definition but that isn't a transducer, if you wanted to keep things simpler

john00:05:35

I'm afk rn but I'd probably end up doing a mix between @U0739PUFQ's and @UGH9KH1DF's

πŸ’œ 1
Melody23:05:44

Hi all I am following the play-clj tutorial to learn a little bit about that library, and I am trying to follow the tutorial at this page: https://github.com/oakes/play-clj/blob/master/TUTORIAL.md The verrrrry first step says "Now let's find an image to use as a texture in the game. Find one you'd like to use, such as https://upload.wikimedia.org/wikipedia/commons/8/85/Clojure-icon.png, and save it to the desktop/resources folder. Next, simply change the line where the label entity is being created, so it creates a texture from that file instead:"

(texture "Clojure-icon.png")
->I have created a default play-clj file with lein per the instructions of the guide, but I do not know where in the file core.clj "the line where the label entity is being created" is.... Furthermore, as a part two to this question, even without making changes - I cannot figure out how to make the game actually run. I think I am in the default folder "desktop" with /src/ in it and I have tried many commands to get it to work like lein run -m src/core.clj and clj -M -m src/hello-world.clj and basically all the variants on those commands that I can think of. I will post the core file as a comment on this thread for the first question of where the texture line goes...

Melody23:05:08

This is the default code with one change suggested by the tutorial in the namespace

(:require [play-clj.core :refer :all]
            [play-clj.g2d :refer :all]))

(defscreen main-screen
  :on-show
  (fn [screen entities]
    (update! screen :renderer (stage))
    (label "Hello world!" (color :white)))
  
  :on-render
  (fn [screen entities]
    (clear!)
    (render! screen entities)))

(defgame hello-world-game
  :on-create
  (fn [this]
    (set-screen! this main-screen)))

Melody23:05:52

My guess was to replace entities here in the :on-render

(render! screen entities)))
with the texture line but idk and it doesn't compile (but it doesn't compile anyway even without changes)

hiredman00:05:13

(label ...)is where the label entity is being created

Melody00:05:03

Thank you!