Fork me on GitHub
#beginners
<
2020-12-04
>
st3fan02:12:02

Is there a way to repeatedly call a function, feeding it its own output?

st3fan02:12:43

(foo (foo (foo (foo 42))))

dpsutton02:12:04

Check out iterate. (Iterate foo 43)

st3fan02:12:26

oh so i would grab (last (take 1000 (iterate foo 42))

st3fan02:12:37

Maybe that is nth

👍 3
st3fan03:12:59

Working on an old Advent of Code puzzle .. so odd .. this works (take 2 (iterate simulate-motion system))

st3fan03:12:18

But when I hit 3 .. (take 3 (iterate simulate-motion system))) .. class clojure.lang.LazySeq cannot be cast to class

st3fan03:12:35

Hm again I am bitten by lazy seqs

st3fan03:12:21

(defn simulate-motion [system]
  (->> system
       (system-apply-gravity)
       (map planet-apply-velocity)))

st3fan03:12:38

The last map turns my system (a vector) into a lazy seq

st3fan03:12:11

wonderful!

st3fan03:12:54

I should really just read clojure.core

9
phronmophobic03:12:09

clojure.core is just really interesting generally to see the language build itself

sova-soars-the-sora15:12:44

I have thought about using clojure.core as wallpaper for my house... you are welcome to have that idea :D

Sam Stowers03:12:05

Hi! Please let me know if this is the wrong place, but I'm learning Clojure and have a simple problem I'm mystified by

Sam Stowers03:12:50

Clojure is telling me that two (what appear to be identical) strings are not equal, but only in the context of this one function.

Sam Stowers03:12:38

(defn any-chars-match
  "Checks if any chars in a string match the single char in the second string. If any do, return true. Otherwise return false."
  [comparisonChars checkChar]
  (reduce (fn [passedVal compareChar]
            (if (= compareChar checkChar) ;; This always evaluates false
              (reduced true)
              false))
          false
          (seq comparisonChars)))

phronmophobic03:12:07

I’m away from keyboard but i think the issue is that compareChar is a char and checkChar is a string in your example. does (checkChars “kk” \k) work?

seancorfield03:12:53

That was what I was about to say: you're comparing characters to a string.

seancorfield03:12:03

When you do seq on a string, you get a sequence of characters, so (seq "kk") is (\k \k) and then you're comparing \k against "k" which are not equal @U01FQCBAFGX

Sam Stowers03:12:33

Ah, knew I was missing something dumb - should've checked the types

Sam Stowers03:12:49

Thanks for the quick reply @U7RJTCH6J @U04V70XH6, really appreciate it

dpsutton04:12:47

and remember to play around in your repl (seq "kk") probably would have shed some light

dpsutton04:12:19

to mimic what your (seq comparisonChars) is doing. ironically, the answer is in your variable name 🙂 comparison`Chars`

Sam Stowers06:12:27

I did that very thing in the REPL, but I didnt put 2 and 2 together that chars were a different data type than strings. Learned today to pay more attention to such things lol

Sam Stowers06:12:27

Coming from JavaScript, which doesn't use the distinction

dpsutton06:12:32

yeah. clojurescript is the same way due to that.

dpsutton06:12:40

its a surprise for sure

phronmophobic06:12:06

part of the distinction is driven by using the built in Java types (which dramatically helps performance). not sure if the design would be the same if reusing the built in java string and char types wasn't as beneficial

noisesmith17:12:56

another thing that would have helped is prn instead of println -

(ins)user=> (println ["k" \k])
[k k]
nil
(cmd)user=> (prn ["k" \k])
["k" \k]
nil

3
noisesmith17:12:52

(there's also pr-str for getting the pr behavior inside arbitrary output from println / format)

Sam Stowers20:12:04

How are you all so helpful

Sam Stowers20:12:19

This is my first interaction w/the Clojure community - posted a question and multiple helpful replies in minutes. Never really seen this before. 🙂

noisesmith20:12:33

haha, we try

🙌 3
Sam Stowers03:12:22

Tried logging each value and the comparison, but that still looks identical

Sam Stowers03:12:09

Could someone tell me what I'm missing? Read up on the = function, tried using identical?, still no idea

Matias12:12:49

Trying to make a websocket app using http-kit, Sente and Reitit. Something is broken and don't know what, browser says it fails to connect, gets some 406's back from the websocket requests. The code is still minimal and nearly identical to working Sente examples except I'm not using Compojure, so I'm stumped on what is the problem... The relevant files are router.clj and websocket.clj at https://gitlab.com/salohonka/tic-tac-toe/-/tree/master/src/clj/tic_tac_toe. I've tried to substitute http-kit for Jetty9 and Reitit for Compojure, but no luck :thinking_face:

sova-soars-the-sora15:12:58

Oo a websocket app, nice! Tell me more about how you are hosting your server? nginx? apache? I think websockets need a special outer layer, too.

sova-soars-the-sora15:12:50

Usually I use all of Sente if working on a real-time app, and hack with a machete. But also be mindful that it might need a reverse-proxy (apache) or something similar (for nginx) ... hope that helps. if not, i am confident we can get down to the bottom of it.

Matias16:12:54

Not using any dedicated webserver, it's a hobby project. Using Sente for both back and front, but I don't think the problem lies with Sente itself, as using just http-kit and it's builtin websocket handler also returns 406's

Matias16:12:45

I will try later if it's a configuration/computer problem by making an empty lein project, adding only http-kit and seeing if that will return 406's as well. If it does, I'll start something without websockets

sova-soars-the-sora16:12:42

Oh you're running it locally?

sova-soars-the-sora16:12:20

Hmmmmmm. curl from the command-line can be helpful to see what's coming back, too.

Matias16:12:37

I've tried a websocket tester, not curl in particular, but it does connect successfully to other websockets so i'm sure it's the server. First get/handshake returns 101, but then any request after 406's. If you are interested, clone and run the project

Matias18:12:09

Created an empty project, added http-kit as a dependency and wrote

(defn handler [req]
  (hk/as-channel req {:on-open (fn [_] (println "connection"))
                      :on-receive (fn [_ msg] (println msg))}))

(defn -main [& _args]
  (hk/run-server handler {:port 3000}))
which did work, so the problem is project specific. Since the app is going to be very simple, I might just drop Sente on the backend and use http-kits own websocket handling

sova-soars-the-sora20:12:14

Yeah why not. Chime in again if you run into any issues ^.^

C-Squirrel13:12:54

Hi. I have a newbie question around plumatic.schema. Given this simple schema, how would I indicate either foo or bar must be provided? (one or the other must be there, but not both)

[{
:id s/Int
:name s/Str
:foo s/Str
:bar s/Int
}]

sova-soars-the-sora15:12:44

I have thought about using clojure.core as wallpaper for my house... you are welcome to have that idea :D

st3fan16:12:49

So here you have it .. I'm working on some Python code for work and I just C-c C-c to try out a code snippet

jculp17:12:09

How do I take something like this and turn it into a map: ((foo bar) (baz qux)) -> {:foo "bar", :baz "qux"}

dpsutton17:12:39

is (foo bar) a sequence of symbols?

dgb2317:12:51

generally it would be into

jculp17:12:37

No they are strings. I am trying (map #(into {} %)) but I get:

java.lang.Character cannot be cast to java.util.Map$Entry

alexmiller17:12:23

entries need to either be Map$Entry or 2-element vectors

dpsutton17:12:34

(into {} (map (fn [[k v]] [(keyword k) v])) '(("foo" "bar") ("baz" "qux")))

alexmiller17:12:35

lists don't allow indexed access

Lars Nilsson17:12:29

(zipmap (map keyword (map first coll)) (map second coll)) maybe?

jculp17:12:09

basically im trying to convert a string like “foo:bar baz:qux” into a map {:foo "bar", :baz, "qux"} . I’m using re-seq but maybe there’s an easier way?

dpsutton17:12:53

that seems fine. can also do a straightforward clojure.string/split

(into {}
      (map (fn [s] (let [[k v] (clojure.string/split s #":")]
                     [(keyword k) v])))
      (clojure.string/split "foo:bar baz:qux" #" "))

jculp17:12:36

oh the destructing in that let binding is slick

alexmiller17:12:44

Any time you are using first/second/nth that should be a clue to use destructuring

dpsutton17:12:02

and just to make it clear for this channel, i used clojure.string/split so it was clear what function i was using but you should require that in your namespace and alias it.

jculp17:12:38

(->> (input "resources/day4.txt" #"\n\n")
  (map #(clojure.string/replace % "\n" " "))
  (map #(clojure.string/split % #" "))
  (map #(let [[k v] (clojure.string/split % #":")]
          [(keyword k) v]))
  (map #(into {} %)))
Am I missing something here? clojure.lang.PersistentVector cannot be cast to java.lang.CharSequence

noisesmith18:12:29

you are calling a string operation on a vector

noisesmith18:12:47

if you look at the stack trace it should show which step is actually doing so

dpsutton18:12:17

your split will return a vector and then you're calling split on that

dpsutton18:12:06

always helpful to do all of the mapped functions on a single thing and make sure it works and then map them over the whole collection. and a sequence of maps like this you could just combine them into one function

st3fan18:12:49

I did a very different solution for parsing the input .. check the Answers thread on #adventofcode 🙂

dpsutton18:12:03

ie, (->> col (map f) (map g)) could just be (map (comp g f) coll). then its very easy to see that the composition works in the single case and therefore necessarily works on the collection

dpsutton18:12:39

(and here don't literally use comp but make a single function which does what you want to "one thing")

noisesmith18:12:34

yeah - that stack of #() blocks is much less clear than a single function doing each of those steps would be

noisesmith18:12:31

in fact, you probably want (->> (input ...) (map (fn [el] (-> el (string/replace ...) (string/split ...) ...))) you can stil use an arrow, but do it in the more useful place

dpsutton18:12:26

and stylistically, make a function that takes the input file and returns a sequence of the lines (doing all the #"\n\n" stuff and splitting. then a single function that processes a single line. and then (map process-line (get-input "resources/day4.txt")). makes it very easy to call process-line on a literal "foo:bar baz:quux" to check yourself

dpsutton18:12:36

an example here if you wanted to see

mathpunk18:12:50

What's a nice function for taking a seq as input and returning all (unordered) pairs where no element is paired with itself?

mathpunk18:12:20

I thought about math.combinatorics, then thought I'd write a nested for, then noticed I'd get more pairs than i wanted

noisesmith18:12:28

(into #{} (for [a lst b lst :when (not= a b)] #{a b}))

mathpunk18:12:53

:male-cook::skin-tone-2: 💋

noisesmith18:12:02

(ins)user=> (def lst (range 10))
#'user/lst
(ins)user=> (into #{} (for [a lst b lst :when (not= a b)] #{a b}))
#{#{6 3} #{7 1} #{4 3} #{0 1} #{7 5} #{7 6} #{3 5} #{0 4} #{0 9} #{0 7} #{5 8} #{7 3} #{4 8} #{0 3} #{2 8} #{9 5} #{9 8} #{6 5} #{0 6} #{1 4} #{1 8} #{6 8} #{7 4} #{6 9} #{4 6} #{6 2} #{7 2} #{3 8} #{1 6} #{1 5} #{4 2} #{7 9} #{1 3} #{1 2} #{0 2} #{4 9} #{3 9} #{1 9} #{2 5} #{4 5} #{7 8} #{3 2} #{2 9} #{0 8} #{0 5}}

noisesmith18:12:58

rule 0 of clojure: use the data type that gets the closest to the behavior you need

dpsutton18:12:36

(let [coll [:a :b :c :a]]
  (for [i (range (count coll))
        j (range (count coll))
        :when (not= i j)]
    (into #{} [(nth coll i) (nth coll j)])))

dpsutton18:12:02

not sure if that's important for you. some of these sets would be single element sets

noisesmith18:12:49

I took "no element is paired with itself" to mean equality, not input position

noisesmith18:12:40

based on "unordered pairs"

mathpunk18:12:41

the source collection happens to be distinct

mathpunk18:12:15

and yeah the operation i'm feeding this data to is commutative

yiorgos19:12:19

Hello, when I do (Integer/parseInt "1") I get back 1 but when I do (map Integer/parseInt ["1" "1"]) I am getting the following error Unable to find static field: parseInt in class java.lang.Integer

didibus19:12:42

(map #(Integer/parseInt %) ["1" "1"])

alexmiller19:12:33

Integer/parseInt is not a function but a java static method

alexmiller19:12:45

wrap it in an anonymous function

alexmiller19:12:53

(map #(Integer/parseInt %) ["1" "1"])

yiorgos19:12:03

Oh I see, thank you very much guys!!!

yiorgos19:12:17

I need to write that down in my Clojure notes 😄

didibus19:12:03

There's also a function I think in core (can't remember which) to convert a method to a function, but just wrapping it in an anonymous function is more common and convenient most of the time.

👍 3
alexmiller19:12:47

memfn is that but generally it's not worth using because it introduces reflection

✔️ 3
didibus19:12:44

Hum... so it does something funkier then just doing (fn ~name [& args#] (. method [email protected]#))

alexmiller20:12:11

well it's effectively same

alexmiller20:12:28

it's ensuring that type hints on memfn will transfer

didibus20:12:36

Oh, so doing that somehow limits the ability of the compiler to not use reflection? Versus wrapping it in an anonymous function?

alexmiller20:12:15

type hints on memfn will work (because the impl ensures they are transferred into the interop call)

alexmiller20:12:34

there's nothing magical here wrt the compiler or reflection

didibus21:12:43

Hum, I guess I'm just confused then, say Class/foo is overloaded and I do:

(map #(Class/foo %) coll)

;; or

(map (memfn Class/foo) coll)
Won't they both use reflection?

didibus21:12:00

Oh, I guess memfn doesn't even work for static

didibus21:12:02

But ok, ignoring that, is there any difference in terms of reflection between:

(map (memfn toUpperCase) ["a" "b"])
("A" "B")
;; and
(map #(.toUpperCase %) ["a" "b"])
("A" "B")

Lars Nilsson19:12:18

Could use (map clojure.edn/read-string ["1" "2"])

yiorgos19:12:15

is there any difference between using edn reader and a java static method?

seancorfield19:12:06

edn/read-string will read pretty much Clojure form -- hash maps, vectors, as well as numbers, strings etc. Generally, your Java static methods are going to parse just one specific thing.

didibus20:12:27

It depends on your use case. They parse thing differently. If you want an Int back, better use Integer/PartseInt. If you are okay getting any type of number back (or possibly even other types like getting a string, a vector, erc. Then you can use edn read-string. Where as ParseInt will throw an error if the thing you have can't be parsed as an Integer.

alexmiller20:12:32

well, read-string will never return an Integer, only Long :)

😮 3
phronmophobic20:12:20

(clojure.edn/read-string {:readers {'an-int int}} "#an-int 42")
😛

alexmiller20:12:19

....for some definition of "never" :)

😁 3
😂 3
Lars Nilsson20:12:12

Or BigInt if the number is big...?

alexmiller20:12:06

for this particular case, I would use Integer/parseInt (or Long/parseLong)

yiorgos20:12:19

Thank you all! Very much appreciated the extra info 👍

Jeff Evans20:12:05

I can conditionally add something to a vector based on a condition, with when.

(def whatev true)
[1 2 3 (when whatev 4)]
Is it possible (similarly) to conditionally add a map entry using when? This doesn’t work (fails with Map literal must contain an even number of forms):
{:a 1 :b 2 (when whatev (:c 3))}

dpsutton20:12:47

that unconditionally adds either nil or 4

dpsutton20:12:39

(cond-> [ 1 2 3] whatever (conj 4)) and (cond-> {:a 1 :b 2} whatever (assoc :c 3)) are the standard ways to do this

👍 3
Jeff Evans20:12:51

ah, that makes more sense. thank you

uosl13:12:55

(merge {:a 1 :b 2} (when whatever {:c 3})) and (into [1 2] (when whatever 3)) are also alternative ways to do this.

st3fan22:12:10

Is there a partition-by that can throw out the “markers” ?

st3fan22:12:56

For example I partition on nil but don’t want the nils. I use take-nth to skip them now. Would be nice to leave that out.

st3fan22:12:01

Maybe the answer is: don’t be afraid to compose pipelines

6