Fork me on GitHub
#beginners
<
2017-06-16
>
victora21:06:36

How would you code the following function in clojure?

def print_statement(operation_list):
  last_date = ""
  for operation in operation_list:
    if operation.date != last_date:
      print(operation.date)
      last_date = operation.date
    print(operation.description)

noisesmith21:06:03

definitely a reduce

noisesmith21:06:39

(reduce (fn [last-date operation] …) "" operation-list)

noisesmith21:06:53

the part in the .. should be pretty simple

victora21:06:33

But in this reduce last-date would be the whole string

victora21:06:37

so i can't check for equality

noisesmith21:06:56

with reduce you decide what to pass as the next first arg

noisesmith21:06:20

so you’d have (fn [last-date operation] .... (:date operation)) (with the if etc. of course)

noisesmith21:06:31

so you only get the date for the next one, as expected

victora21:06:41

All right, yes of course, my mistake. But what if I need to return a string, instead of printing?

noisesmith21:06:50

send to who?

noisesmith21:06:08

so you also need a collection of strings?

victora21:06:11

change print_statement to return the string printed

noisesmith21:06:22

{:last-date “..” :statements […]}

noisesmith21:06:31

then you construct the hash map to return at each step

victora21:06:54

def statement(operation_list):
  statement = ""
  last_date = ""
  for operation in operation_list:
    if operation.date != last_date:
      statement += operation.date
      last_date = operation.date
    statement += operation.description

victora21:06:09

Oh, so the reduce actually receives and returns a pair

victora21:06:29

Ok, that makes sense.

noisesmith21:06:31

it receives whatever you want it to - yes - so you can use a hash-map if you need to track multiple values

noisesmith21:06:02

or you can use loop with rest, but if I am consuming a coll one item at a time I prefer reduce

victora21:06:13

I know it can receive whatever I want, but I wanted to know the right 'clojurian' way to do it 🙂

noisesmith21:06:27

the trade off is that loop can have separate bindings without a collection, and reduce doesn’t make you step through the collection by hand

victora21:06:31

noisesmith: Indeed, but since hash-maps are pretty efficient, it's more or less the same

noisesmith21:06:59

right - I think most people would choose reduce, just mentioning loop can make sense here

victora21:06:18

but loop is way more low-level programming, right?

noisesmith21:06:19

yeah - and it’s not just lower level, it’s also slower than reduce, believe it or not

noisesmith21:06:32

(for consuming collections at least)

victora21:06:24

Incredible!

victora21:06:57

just like there's one (and preferably obvious) way to do it in python

victora21:06:38

How am I getting floating point "errors" while using bigdec?

victora21:06:58

balance: 69.5700000001

guy21:06:47

what error do you get?

victora21:06:05

I'm summing quantities that should add to 69.57

victora21:06:16

and they are bigDecimals, but it's adding to 69.5700000001

guy21:06:49

So its an error with the result not like an exception then yeah?

guy21:06:37

what are the numbers you are trying to add up?

victora21:06:06

It's a list [23.19 23.19 23.19]

victora21:06:12

i'm using reduce to sum it, and they are bigdec's

guy21:06:27

mmm thats interesting

guy21:06:30

with that collection i get

victora21:06:09

nvm, my mistake was stupid. The initial value was 0.0 not (bigdec 0.0)

victora21:06:18

strange that it falls back to double, instead of converting 0.0

noisesmith21:06:44

this is one reason I like OCaml not having polymorphic arithmetic - it’s a pain in the ass but you always know exactly which numeric conversion is happening where

seancorfield21:06:01

@noisesmith: I got caught out by Clojure’s promotion/demotion behavior around Double/`BigDecimal` just the other day 😞 @victora

victora21:06:25

Good to know it's not just me 😂

seancorfield21:06:33

cough threads cough 😸

guy21:06:44

@victora i think its because the two values get converted into 'numbers'

victora21:06:27

guy: That's the clojure type right?

victora21:06:41

If they are not all the same, clojure tries to add them itself, maybe?

guy21:06:12

yeah they get casted straight away to Number in the clojure code

guy21:06:30

then here

seancorfield21:06:16

(type (+ 1.0 1.0M)) => java.lang.Double which I think is very surprising…

guy21:06:33

im still working out why its a double not a long tbh

noisesmith21:06:37

this should be done directly in byte code the same way java would do it - for better or worse

noisesmith21:06:14

Number is a java type, and it’s the superclass for all possible args to +

guy21:06:54

sure but why does it return a double

guy21:06:58

not a long

guy21:06:00

:thinking_face:

noisesmith21:06:22

wait, which code do you think would return a long?

seancorfield21:06:26

Clojure never converts floating point to integer types implicitly.

seancorfield21:06:50

(but it does convert BigDecimal to Double implicitly, it seems)

guy21:06:10

@noisesmith the + code i linked at the top

guy21:06:36

mm let me double check im not being silly

noisesmith21:06:56

adding a long to a double returns a double - whether java or clojure

guy21:06:51

ok so that must be why when u add a double and a bigdec it does the same?

guy21:06:55

that makes sense to me

guy21:06:57

:thumbsup:

victora21:06:59

(+ 1.0M 0.0) should def. return a bigDecimal though

victora21:06:20

Why would someone bother with bigDecimal if they wanna convert it back to double?

noisesmith21:06:53

devil’s advocate: why would you use floating point data anywhere in your calculation if it needed to be precise?

guy21:06:48

then added together

victora22:06:18

Pretty stupid question I couldn't find on google: how can I "include/import" another file?

victora22:06:25

I know it's with require, but can't get the right syntax

victora22:06:26

Thanks @mathpunk , I'll give it a shot

mathpunk22:06:33

note: if you are using protocols, a fact that surprised me is you have to use import with them

victora22:06:03

I have two files balance/db.clj balance/handler.clj in handler.clj I'm using (:require [balance.db :as db]) I get a 'not found' error, why?

noisesmith22:06:28

so I assume the balance directory is on your classpath?

noisesmith22:06:23

what specific error do you get?

victora22:06:35

Exception in thread "main" java.lang.Exception: namespace 'balance.db' not found after loading '/balance/db', compiling:(balance/handler.clj:1:1)

noisesmith22:06:58

so that means that it loaded your file, but there was no (ns balance.db) form in the file

noisesmith22:06:01

and require doesn’t like that

noisesmith22:06:29

if you just want to load a file, there’s load-file, but require really wants to know if a namespace exists already, and keep track of what’s loaded

victora22:06:39

Hmm, shouldnt the

balance
come from the path? I'm using
(ns db)

noisesmith22:06:50

that’s not how it works

victora22:06:02

how do I highlight without taking the entire line, btw?

noisesmith22:06:36

if you called (require [balance.db ...]) it needs (ns balance.db ...) - you can highlight inline with `

victora22:06:11

Yeah, it worked this way. I thought "." was used to indicate directory

noisesmith22:06:38

yeah, directories are meant to map to namespace substrings and visa-versa, and things fall apart if they are not in sync

noisesmith22:06:06

though when experimenting I’ll often make /tmp/foo.clj and then run (load-file "/tmp/foo.clj") in the repl

victora22:06:31

What would you guys use to run a background task that should run every day at 00:00am ?

victora22:06:13

straight to java huh

noisesmith22:06:30

it’s a straightforward api

noisesmith22:06:57

wherever it asks for Callable you can pass in a clojure function

noisesmith22:06:22

same with Runnable

victora22:06:25

damn, pretty nice

noisesmith22:06:46

yeah, it’s not complicated to use thanks to those facts

noisesmith22:06:28

the only part that is mildly annoying is the TimeUnit arg required, but that’s tiny as java inconveniences go

victora22:06:47

scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) If I read the API right, I only need to call this method

victora22:06:48

is that right

noisesmith22:06:06

well, you need an instance of the scheduler, but yes, that’s it

noisesmith22:06:19

and it returns a handle to your scheduled thing

victora22:06:57

Do you have example code lying around? That could help. First time doing heavy java/clojure integration heh

victora22:06:19

Actually, I found something better

noisesmith22:06:26

(def scheduler (ScheduledThreadPoolExecutor. 12)) (defn schedule [f delay-ms period-ms] (.scheduleAtFixedRate f delay-ms period-ms TimeUnit/MILLISECONDS))

victora22:06:29

Pretty straight forward, and uses the same API you use

noisesmith22:06:51

yeah, I just prefer to use the API when using it is two lines of code

victora22:06:05

I'll get to work