This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-12-18
Channels
- # adventofcode (62)
- # aws (5)
- # beginners (59)
- # calva (63)
- # cider (26)
- # cljdoc (1)
- # cljsrn (22)
- # clojure (99)
- # clojure-austin (1)
- # clojure-dev (19)
- # clojure-europe (4)
- # clojure-hamburg (2)
- # clojure-italy (3)
- # clojure-nl (23)
- # clojure-spec (2)
- # clojure-uk (85)
- # clojurescript (41)
- # core-async (17)
- # cursive (20)
- # data-science (11)
- # datascript (2)
- # datomic (31)
- # emacs (7)
- # figwheel (28)
- # figwheel-main (12)
- # graphql (2)
- # hyperfiddle (3)
- # juxt (1)
- # kaocha (2)
- # leiningen (5)
- # nrepl (13)
- # off-topic (45)
- # pathom (13)
- # pedestal (11)
- # re-frame (20)
- # reagent (10)
- # shadow-cljs (92)
- # spacemacs (9)
- # sql (39)
- # tools-deps (32)
- # unrepl (3)
I believe many implementations of 'round' round down <odd_integer>+0.5, but round up <even_integer>+0.5 (or vice versa), in an attempt to make the round error more 'balanced'.
Very likely some folks who do lots of calculations with floating point numbers have done some kind of numerical analysis kind of thinking to determine that this is in some ways better than always rounding down, or always up.
I've yet to hear of a good argument for this, except: people calling 'round' function are sloppy, so we will make assumptions about their data, and then show that round-to-even is good.
It seems that for the person doing the analysis, you should never call a function unless you know exactly what it's supposed to do.
Probably way more than most people care to know about this topic here: https://en.wikipedia.org/wiki/Rounding
I agree that people should not pick one of the alternatives mindlessly.
In some programming environments, I'd bet you $1 that round() is explicitly specified, and doesn't always round half up, or always round half down. Otherwise that Wikipedia page wouldn't have so many alternatives on it.
I don't want to bet real $ (or crypto); I do think that people should explicitly use floor/ceil instead of calling round
; and one can get round-to-nearest behaviour via simply floor(x + 0.5);
If someone determined that it was better to round to nearest odd integer in their application, you would deny them this choice?
No, I would prefer they define a round_to_nearest_odd function instead of just calling "round" š
I'm saying the rounding behaviour of your program should be explicit and not dependent on standard lib impl of round.
Let's call truce on this, somehow I think we're arguing over something that increases neither of our productivity. š
That looks pretty explicit, even though it doesn't require saying what mode you want on every call -- you may choose to do it once for your whole application
I was in truce the whole time š
I don't know what you mean by she skew to round numbers. But taking the floor or ceiling of every measurement would certainly put pressure in only one direction on your measurements
Your argument boils down to: here is a distribution where round_tlo_even is better than floor. Therefore, round
should do round_to_even
I am saying: we should have explicit functions floor, ceil, round_to_nearest_int, round_to_nearest_even, and not have a round
function at all.
Not sure of any language that agrees with you. Most rounds are documented which way they go and often configurable with a strategy. The default is often round middle to even. But most languages also have floor and ceiling so your fears are assuaged as well
Well, if python did what I suggested, it wouldn't have the problem demonstreated above on 2.7 vs 3.0 rounding that started all this.
I agree they should have done rounding correctly to begin with. But I like that they fixed their mistake :)
I'm just partial to round to middle because of my chemistry classes back in high school. Preventing systemic bias is important in the physical science
I dont mind that they use one or another, but if you change the maths of your language then may just as well change the name :)
they explicitly said that Py3 wasnāt backwards compatible with Py2 tho
I think that given that they wanted to break compatibilty, Python actually found one of the more sane ways to do so
Story time! So a few days ago, a friend shared this Python snippet with meā¦
Impressed, I thought āWhat would that look like in Clojure?ā After a few questions in #beginners I ended up with
(ns map-comp.core
(:require [clojure.pprint]
[clojure.string :refer [split]]))
;; Functions to replicate python's os.walk functionality
(def directory? (memfn ^java.io.File isDirectory))
(def list-files (memfn ^java.io.File listFiles))
(defn separate-dirs-from-files
[files-list]
(reduce (fn [[dirs files] file]
(let [name (.getName file)]
(if (directory? file)
[(conj dirs name) files]
[dirs (conj files name)])))
[]
files-list))
(defn build-file-tree
[^java.io.File file]
(cons file
(separate-dirs-from-files (list-files file))))
(defn file-tree-seq
[root-dir]
(->> root-dir
()
(file-seq)
(filter directory?)
(map build-file-tree)))
;; Replicates the functionality of the python snippet
(defn relative-name
[path dir]
(let [dir-path (str dir)]
(string/replace dir-path (re-pattern path) "")))
(defn directory-with-no-files?
[[file dirs files]]
(empty? files))
(defn pair-file-with-dirs
[root-path [file dirs]]
[(relative-name root-path (.getPath file))
(vec dirs)])
(defn dir->map
[root-path]
(->> (file-tree-seq root-path)
(drop 1)
(filter directory-with-no-files?)
(map #(pair-file-with-dirs root-path %))
(into {})
(clojure.pprint/pprint)))
(comment
(dir->map "/Users/jay/Projects/map-comp/test/fixtures"))
Realizing that is just not the same I realized I could write a library that behaves like Pythonās os.walk, then another library to add map comprehension as a macro:
(ns example.core
(:refer-clojure :exclude [replace])
(:require [file-tree.core :refer [walk-seq]]
[for-map.core :refer [for-map]]
[clojure.string :refer [replace]]
[clojure.pprint :refer [pprint]]))
(defn relative-name
[root path]
(replace path (re-pattern root) ""))
(def root-dir "/Users/jay/Downloads/test/resources")
(for-map [[path dirs filenames] (walk-seq root-dir)
:let [parent (relative-name root-dir path)]
:when (and parent (empty? filenames))]
{parent dirs}))
Now a Clojure, has map comprehension too šIn retrospect itās kind of a shit story. Needs a better villain⦠which Iām pretty sure is me. But it is my first published macro!
I did learn a lot. It REALLY reinforced that Clojure code is just data and that with tools like macros, we can evolve the language to our problems. If itās valuable, it will catch on; otherwise, time will pass it by. It will still work for a while probably, even without maintenance. Thereās something beautiful about that.
(defmacro for-map
"Shortcut for putting the results of a for block into
a map. Requires that each iteration in the for resolve
to a vector of two items [key value].
(for-map [x (range 3)]
[x (inc x)])
;; => {0 1, 1 2, 2 3}
(for-map [x (range 8)
:when (even? x)]
[x (* x x)])
;; => {0 0, 2 4, 4 16, 6 36}"
[bindings & exprs]
(when-not (vector? bindings)
(throw (IllegalArgumentException.
"for-map requires bindings to be in a vector.")))
(when-not (even? (count bindings))
(throw (IllegalArgumentException.
"for-map requires an even number of forms in binding vector.")))
`(into {}
(for ~bindings
(let [result# (do [email protected])]
(if-not (and (vector? result#) (= (count result#) 2))
(throw (IllegalArgumentException.
(str "The expression in a for-map must evaluate to a vector of two items. "
"Got "
result#)))
result#)))))
@U3JH98J4R Hah! Thatās awesome. Mine is a very naive implementation https://github.com/jayzawrotny/for-map/blob/master/src/for_map/core.clj
Should I be validating from a macro similarly to yours?
I think validating what you can statically is nice, since errors during macro expansion are annoying. I have one check in there which occurs at runtime, which is not ideal. I am considering maybe restricting the macro even more by making it so that the body of for-map has to be a 2 item vector at macro time
clojure
;; Without explicit error check
(for-map [a (range 10)]
(list a (inc a)))
Execution error (ClassCastException) at rubidium.quotes/eval9283 (form-init5967951676652970669.clj:1).
java.lang.Long cannot be cast to java.util.Map$Entry
;; With explicit error check
(for-map [a (range 10)]
(list a (inc a)))
Execution error (IllegalArgumentException) at rubidium.quotes/eval9476$iter$fn$fn (form-init5967951676652970669.clj:1).
The expression in a for-map must evaluate to a vector of two items. Got (0 1)
@U3JH98J4R Thank you, that makes sense, the second one is a lot more clear. Iāll add some similar validation if nothing else but for practice. I take it static validation means before the macro returns a list of expressions?
Also, we even used the same examples in our documentation. Craziness.