Fork me on GitHub
#beginners
<
2022-02-09
>
Banseok Yeo05:02:14

Hello, I'm new to Clojure & FP. I wanted to know what is the best way to conditionally apply map to collections in Clojure. for example,

(def d (list 1 2 3 4)
(cond-map odd? inc d)
=> (2 2 4 4)
Thanks for any advice

dpsutton05:02:48

just "distribute" the cond into the mapped function. (map #(if (odd? %) (inc %) %) [1 2 3 4]). If this pattern happens often make a little combinator for it

dpsutton05:02:38

(defn func-if [f g] (fn [x] (if (f x) (g x) x)))
(map (func-if odd? inc) [1 2 3 4])

dpsutton05:02:38

(map #(cond-> % (odd? %) inc) [1 2 3 4])

Banseok Yeo06:02:58

thanks for your help. I have an additional question. does map always have a space complexity of O(2n)?

octahedrion09:02:38

(sequence (comp (filter odd?) (map inc)) d)

tomd09:02:49

@U0CKDHF4L that removes the even numbers rather than keeping them unchanged

octahedrion10:02:04

sorry yeh didn't read the question properly

octahedrion10:02:06

(interleave
    (sequence (remove odd?) d)
    (sequence (comp (filter odd?) (map inc)) d))
I guess the disadvantage of this is it's longer and tests odd? twice

Max Deineko11:02:00

@U02U505JGVD yes, as long as you don't place additional restrictions on the applied function(s), map necessarily has space complexity of O(2n) = O(n). (Sorry, that was not accurate enough to be correct as stated.) As long as you don't place additional restrictions on the (class) of applied function(s), map itself necessarily has space complexity of Ω(n), which the result alone requires. Of course, the additional space complexity is also 𝓞_(2n) = 𝓞(n) (and thus ϴ(n)_).

👍 1
James Amberger14:02:41

Is there some kind of online apropos style feature in the repl or in rebel etc?

andy.fingerhut15:02:45

user=> (apropos "find")
(clojure.core/find clojure.core/find-keyword clojure.core/find-ns clojure.core/find-protocol-impl clojure.core/find-protocol-method clojure.core/find-var clojure.core/re-find clojure.repl/find-doc)

andy.fingerhut15:02:59

If you do not get that behavior in your REPL, you might need to do something like (use 'clojure.repl) first

andy.fingerhut15:02:22

That has been part of Clojure for at least 10 years now)

James Amberger15:02:43

Ha, should have guessede

noisesmith17:02:13

check out dir also - and some of these:

Clojure 1.10.1
(ins)user=> (dir clojure.repl)
apropos
demunge
dir
dir-fn
doc
find-doc
pst
root-cause
set-break-handler!
source
source-fn
stack-element-str
thread-stopper
nil

noisesmith17:02:39

I use find-doc and source frequently, they provide the parts of an IDE I would actually use if I used one

James Amberger16:02:57

Guys before I throw this machine out the window, how does one import and use clojure’s string functions? Does it need to be in deps.edn? (ns hello (:require [clojure.string :as string]))

andy.fingerhut16:02:50

In a source file, I would recommend something like this:

(ns my.namespace.name
  (:require [clojure.string :as str]))

(str/join ["a" "b"])

andy.fingerhut16:02:12

If you want to do it in a REPL, you can also do the above, but it will create a new namespace and switch you to it right there in the REPL if you do that. If you do not want to switch namespaces in the REPL, you can do (note slightly different syntax): (require '[clojure.string :as str])

andy.fingerhut16:02:09

And please don't throw your machine out the window. Give it to me instead 🙂

James Amberger16:02:31

Thanks. That all makes sense to me. Must be something wrong with my editor config because it’s just not cooperating, but the code itself is fine, compiles/runs.

andy.fingerhut16:02:07

The more automagical stuff in your editor/IDE, it can get confusing when things don't go as you wish. There are editor/IDE-specific channels on this Slack that might be helpful to ask in, if you get into issues that you think might be specific to your setup, e.g. #emacs #calva #cursive etc.

James Amberger16:02:06

Thanks I may have to check that out. I see there is #vim, that’s good

James Amberger16:02:22

There’s something I’m just not getting about (ns foo (:require…))

flowthing18:02:08

The ns form is a bit tricky at first. https://gist.github.com/ghoseb/287710/ might help.

James Amberger16:02:57

Here is what I have: `(ns hello (:require [clojure.string :as string] [buddy/buddy-core]))`

andy.fingerhut16:02:22

I don't think that buddy/buddy-core is a valid namespace name in Clojure

andy.fingerhut16:02:42

It might be a valid way of naming a project in Maven, or deps.edn, but not in Clojure source code.

andy.fingerhut16:02:35

project names in Maven/deps.edn/project.clj are often based on Maven or Clojars rules for projects. A single project can contain any number of Clojure namespaces, and those namespace names might have zero relationship to the project name (but they usually are related, by convention)

James Amberger16:02:48

so having specified the dep

James Amberger16:02:57

I have to go check the docs or code for the namespace

dpsutton16:02:13

You had this confusion yesterday as well and I’m not sure we’ve resolved it for you. Coordinates for dependencies are a way to specify that dependency and for some program to download those dependencies to your computer. buddy/buddy-core and some version number can be resolved to a jar file (called the coordinates). It’s a way to specify that jar file, but does not specify what is in that jar file. The jar file is basically a zip file with a bunch of clojure files in it. And the namespaces of those clojure files have no relationship to the name of the dependency’s coordinates.

1
James Amberger16:02:32

Thanks to you both. back on track.

🎉 1
Alex Miller (Clojure team)16:02:19

I made a note to better explain the linkage between deps and namespaces at https://github.com/clojure/clojure-site/issues/575 on the relevant Clojure pages. This is a common area of confusion for beginners.

Alex Miller (Clojure team)16:02:26

So thanks for asking the question!

👍 1
Ngoc Khuat16:02:00

(.getYear (new java.util.Date)) => 122 why does this return 122 ? is it offset from 1900 ?

dpsutton16:02:05

should be using java.time rather than java.util.Date though

Ngoc Khuat16:02:21

I was looking for a way to do datetime without adding dependencies, but seems like I should now…

dpsutton16:02:28

java.time is in the jvm since java 8 i believe

dpsutton16:02:51

just stick with interop on java.time and you will have no dependencies

Ngoc Khuat16:02:32

ahhh, got it (.getYear (java.time.LocalDateTime/now)) => 2022 Thanks Dan 🙏

🙏 1
ghadi17:02:38

to be super precise: java.time meaning the jdk package (There have been a couple of horribly named clojure libraries like clojure.java-time which add to confusion)

🙏 2
James Amberger20:02:12

What’s a single quote when found postfixed to a symbol?

dharrigan20:02:32

like this a'?

hiredman20:02:15

It is nothing, just part the symbol's name

dharrigan20:02:16

It's a concept borrowed from maths (at least I think) called prime, basically, in Clojure it has no special meaning, it's a valid symbol name, but I generally use it if I'm using the same variable name (where it makes sense contextually), i.e., name and perhaps in a let I rebind it to name'

dharrigan20:02:31

Here's the relevant wikipedia on Prime

James Amberger20:02:32

ahh yeah that’s not something i am used to yet

James Amberger20:02:43

having these characters in symbols

James Amberger20:02:50

hex->string and whatever

dharrigan21:02:11

In mathematics, the prime is generally used to generate more variable names for similar things without resorting to subscripts, with x′ generally meaning something related to (or derived from) x.

the-alchemist21:02:06

(slightly off-topic 🙇). any beginners interested in a part-time Clojure internship? DM me

lspector22:02:43

@james.amberger, in Clojure core there are some numeric functions that end with a prime (`'`), for which you'll see in the docs: "Supports arbitrary precision." For example, for *':

(* 1234567890 9876543210)
;; ArithmeticException integer overflow
(*' 1234567890 9876543210)
;;=> 12193263111263526900N

brendnz22:02:22

Just heard about PutIfMissing in java (add a key-value pair to a map if the key does not already exist). Does Clojure have an equivalent function in its map data structure arsenal? An empty Google search suggests not.

paulocuneo23:02:40

Don't know if this is a good pattern/idiom :thinking_face:, but you can use fnil with update

(update {} :a-missing-key (fnil identity "default"))
(update {:some-key "value"} :some-key (fnil identity "default"))

brendnz12:02:47

Interesting, thankyou Paul, but not the functionality I was thinking about. I am happy with hiredman's answer.

hiredman22:02:29

put if missing is most useful in mutable maps where it is used to provide atomic insertion when a value is missing

💯 1
hiredman22:02:07

an immutable map doesn't really need to do that

💯 1
❤️ 1
Antoine Viscardi23:02:59

Hi there! I am wondering if there is a way to create a partial function by filling the right most arguments instead of the left most. My use-case is to create a div10 function that simply divides by 10. I know this is trivially achieved with something like (defn div10 [n] (/ n 10)). However, I'd like to be able to write something like (partial (/ _ 10)) or (rpartial (/ 10) if that makes any sense at all.

Antoine Viscardi23:02:14

Or is it the case that partial really is just meant to be syntactic sugar for the case when you want to fill arguments from the left?

James Amberger23:02:24

I see the style guide prefers partial over a lambda when possible. Why?

Alex Miller (Clojure team)23:02:25

Wouldn't be my preference :) I actually probably use fn the most these days

2
☝️ 1
quoll04:02:29

partial is actually slower than using #(…) (not that this matters unless you're in “hot” code). The primary difference is that the function resulting from partial is a varargs function. So, to add 10 to a number you could say: #(+ 10 %) And apply that. On the other hand, if you use (partial + 10) then it will just add 10, or you could add more: (let [f (partial + 10)] (f 5 7)) gives 22

1
quoll04:02:09

After learning SICP, it was my inclination to use partial, but after being questioned on it I tested it for performance against a function that closed over the values to be bound, and found that partial was slower. It only mattered because this was code that was in a tight loop

1