clojure refactor of calendar vector - clojure

I have come up with the following function that when given a start date will return 5 vectors, each of 7 days long.
You can think of this as of any calendar month that is generally displayed like this:
(defn weeks [start-date]
(for [week-counter (range 0 35 7)]
(vec (for [day-counter (range 7)]
(->
start-date
(.clone)
(.add "days" (+ week-counter day-counter))
(.format date-format))))))
I am new to clojure and I am curious of other ways I could have written this.

I'd be tempted to express it like this:
(defn weeks [start-date]
(letfn [(compose [n] (-> start-date
(.clone)
(.add "days" n)
(.format date-format)))]
(map vec (partition 7 (map compose (range (* 7 5)))))))
But is this what you want? For illustration, if we simplify the compose function ...
(defn weeks [start-date]
(letfn [(compose [n] (str start-date " " n))]
(map vec (partition 7 (map compose (range (* 7 5)))))))
... then, for example,
(weeks "august")
... produces
(["august 0"
"august 1"
"august 2"
"august 3"
"august 4"
"august 5"
"august 6"]
...
["august 28"
"august 29"
"august 30"
"august 31"
"august 32"
"august 33"
"august 34"])
I find it hard to think of a use for this.
If you want the dates of a month week-by-week, you need to know
the day of the week that the first of the month falls on - a number
from 0 to 6;
the number of days in the month.
A function to generate the pattern is
(defn week-pattern [start-day days-in-month]
(take-while
(partial some identity)
(partition 7 (concat (repeat start-day nil)
(range days-in-month)
(repeat nil)))))
For example, June this year started on a Sunday, day 6 (if a week starts on Monday), so spanned six weeks, though only thirty days long:
=> (week-pattern 6 30)
((nil nil nil nil nil nil 0)
(1 2 3 4 5 6 7)
(8 9 10 11 12 13 14)
(15 16 17 18 19 20 21)
(22 23 24 25 26 27 28)
(29 nil nil nil nil nil nil))
With this as a template, you can add function arguments to map and/or map-indexed to play around with the contents at will.
I have followed your usage in counting days of the month from zero instead of from 1 as a real calendar would. If you want to count from 1, replace (range days-in-month) with (range 1 (inc days-in-month))
If you have the choice, consider using a library such as Joda-Time that treats dates as immutable values.

Related

lang.LazySeq cannot be cast to IPersistantVector

In the process of learning Clojure.
I have a function to draw a random card from the deck
(defn draw-random-card
[cards]
(let [card (rand-nth cards)
index (.indexOf cards card)]
{:card card :remaining-cards (concat (subvec cards 0 index)
(subvec cards (inc index)))}))
Running it:
(draw-random-card ["Ace" 2 3 4 5 6 7 8 9 10 "Jack" "Queen" "King"])
=> {:card 4, :remaining-cards ("Ace" 2 3 5 6 7 8 9 10 "Jack" "Queen" "King")}
I'd like to call it twice and get 2 cards out but the second time it calls it, it will pass the reduced deck from the first call.
IN the end I'd like to have the 2 cards and the reduced deck to use later.
I would have thought I could do something like:
(def full-deck ["Ace" 2 3 4 5 6 7 8 9 10 "Jack" "Queen" "King"])
(let [first-draw (draw-random-card full-deck)
first-card (:drawn-card first-draw)
second-draw (draw-random-card (:remaining-cards first-draw))
second-card (:drawn-card second-draw)
remaining-deck (:remaining-cards second-draw)]
(println "First card: " first-card)
(println "Second card: " second-card)
(println "Remaining deck:" remaining-deck))
However, I'm obviously doing something dumb here as I get the error:
Execution error (ClassCastException) at aceyducey.core/draw-random-card (form-init3789790823166246683.clj:5).
clojure.lang.LazySeq cannot be cast to clojure.lang.IPersistentVector
I think the problem is in the line
second-draw (draw-random-card (:remaining-cards first-draw))]
Because remaining-cards isn't a vector?
Which means
concat (subvec cards 0 index)
(subvec cards (inc index)))}))
Isn't returning a vector? Rather a lazy sequence ???
But at this point I'm lost.
Help!
#amalloy makes a good point: the Clojure built-in function shuffle is probably what you want:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test) )
(def cards [:ace 2 3 4 5 6 7 8 9 10 :jack :queen :king] )
(dotest
(dotimes [i 3]
(spyx (shuffle cards))))
=>
Testing tst.demo.core
(shuffle cards) => [:king :jack 6 2 9 10 :ace 4 8 5 3 :queen 7]
(shuffle cards) => [2 :jack 7 9 :queen 8 5 3 4 :ace 10 :king 6]
(shuffle cards) => [7 :queen :jack 4 3 :king 6 :ace 2 10 5 8 9]
This and much more is available at the Clojure CheatSheet. Be sure to bookmark it and always keep a browser tab open with it.
concat returns a lazy sequence. You can coerce it into a vector by using:
(vec (concat ...))
Here is the full code with a test:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn draw-random-card
[cards]
(let [card (rand-nth cards)
index (.indexOf cards card)]
{:drawn-card card :remaining-cards (vec (concat (subvec cards 0 index)
(subvec cards (inc index))))}))
(def full-deck ["Ace" 2 3 4 5 6 7 8 9 10 "Jack" "Queen" "King"])
(dotest
(let [first-draw (draw-random-card full-deck)
first-card (:drawn-card first-draw)
second-draw (draw-random-card (:remaining-cards first-draw))
second-card (:drawn-card second-draw)
remaining-deck (:remaining-cards second-draw)]
(println "First card: " first-card)
(println "Second card: " second-card)
(println "Remaining deck:" remaining-deck))
)
and result:
-------------------------------
Clojure 1.10.0 Java 12
-------------------------------
Testing tst.demo.core
First card: Queen
Second card: King
Remaining deck: [Ace 2 3 4 5 6 7 8 9 10 Jack]
Update:
To be specific, the problem was the call to subvec in the 2nd iteration of your code. Here is an example:
(dotest
(let [vals (vec (range 10)) ; a vector
s1 (subvec vals 2 4) ; so `subvec` works
s2 (subvec vals 6) ; and again
lazies (concat s1 s2)] ; creates a lazy sez
(is= [2 3] (spyxx s1))
(is= [6 7 8 9] (spyxx s2))
(is= [2 3 6 7 8 9] (spyxx lazies))
(throws? (subvec lazies 0 2)))) ; ***** can't call `subvec` on a non-vector (lazy sequence here) *****
with result:
s1 => <#clojure.lang.APersistentVector$SubVector [2 3]>
s2 => <#clojure.lang.APersistentVector$SubVector [6 7 8 9]>
lazies => <#clojure.lang.LazySeq (2 3 6 7 8 9)>
so by coercing the output of concat to a vector, the call to subvec succeeds on the next time through the function.
So, in hindsight, a better solution would have been to coerce the input to a vector like so:
(let [cards (vec cards)
card (rand-nth cards)
index (.indexOf cards card)]
{:drawn-card card
:remaining-cards (vec (concat (subvec cards 0 index)
(subvec cards (inc index))))}))
Update #2
If you don't want to coerce your input to a vector, you can use the .subList() function via Java interop:
(dotest
(spyxx (.subList (concat (range 5) (range 10 15)) 5 10))
(spyxx (.subList (range 10) 2 5))
(spyxx (.subList (vec (range 10)) 2 5))
(spyxx (subvec (vec (range 10)) 2 5))
(throws? (subvec (range 10) 2 5))) ; *** not allowed ***
with result
(.subList (concat (range 5) (range 10 15)) 5 10)
=> <#java.util.ArrayList$SubList [10 11 12 13 14]>
(.subList (range 10) 2 5)
=> <#java.util.Collections$UnmodifiableRandomAccessList [2 3 4]>
(.subList (vec (range 10)) 2 5)
=> <#clojure.lang.APersistentVector$SubVector [2 3 4]>
(subvec (vec (range 10)) 2 5)
=> <#clojure.lang.APersistentVector$SubVector [2 3 4]>

Get the 3 closest values some given number in a list?

Given a list of integers, find the 3 closest values to a given number.
=> (def mylist '(3 6 7 8 9 12 14))
=> (get_closest mylist 10)
(8 9 12)
(letfn [(closest [a b]
(take 3 (sort-by #(Math/abs (- % b)) a)))]
(let [a '(3 6 7 8 9 12 14)]
(closest a 10)))
Following #akond's answer, but as a general function:
(defn closest [x n coll]
"Return a list of the n items of coll that are closest to x"
(take n (sort-by #(Math/abs (- x %)) coll)))
(closest 4 3 (range 10))
; => (4 3 5)
Notice that if coll is a Java array sort-by might modify it.

How to find numbers that occur more than once in a list using clojure

I have a list of numbers 2 4 3 7 4 9 8 5 12 24 8.
I need to find numbers which are repeated more than once in clojure.
I have used frequencies function to find. But the result is
{2 1,4 2,3 1,7 1,9 1,8 2,5 1,12 1,24 1}
I intially thought of considering them as key value and then take each key value once and see if val is > 1. if value is greater than 1 then I need to inc 1.
But I am unable to work this out.
Can anyone please help me??
Is there anyway I can make this into [[2 1][4 2][3 1][7 1][9 1][8 2][5 1][12 1][24 1]] and consider each vector recursively or any better idea you can think of.
Thank you.
The function below will just continue on where you have stuck:
(defn find-duplicates [numbers]
(->> numbers
(frequencies)
(filter (fn [[k v]] (> v 1)))
(keys)))
It will filter map entries that have values greater than 1 and then extract their keys.
(find-duplicates [2 4 3 7 4 9 8 5 12 24 8])
;; => (4 8)
If you want the repeated items:
(defn repeated [coll]
(->> coll
frequencies
(remove #(= 1 (val %)))
keys))
(repeated [2 4 3 7 4 9 8 5 12 24 8])
;(4 8)
If you just want to count them:
(defn repeat-count [coll]
(->> coll
frequencies
(remove #(= 1 (val %)))
count))
(repeat-count [2 4 3 7 4 9 8 5 12 24 8])
;2
You can do it lazily, so that it will work on an endless sequence:
(defn repeated [coll]
((fn ff [seen xs]
(lazy-seq
(when-let [[y & ys] (seq xs)]
(case (seen y)
::several (ff seen ys)
::once (cons y (ff (assoc seen y ::several) ys))
(ff (assoc seen y ::once) ys)))))
{} coll))
(repeated [2 4 3 7 4 9 8 5 12 24 8])
;(4 8)
This is similar to core distinct.
... and finally, for brevity, ...
(defn repeated [coll]
(for [[k v] (frequencies coll) :when (not= v 1)] k))
I stole the use of keys from Piotrek Byzdyl's answer. It is only supposed to apply to a map. but works perfectly well here on a sequence of map-entries.
(->> s (group-by identity) (filter (comp next val)) keys)
You are on the right track.
If you seq over hash-map, e. g. via map, you get the kv tuple structure you described and can destructure an individual tuple in the element transformation function:
(->> s
(frequencies)
(map (fn [[number times]]
(cond-> number ; take number
(> times 1) (inc))))) ; inc if (times > 1), otherwise return number
You can use this approach.
(def c [2 4 3 7 4 9 8 5 12 24 8])
(->> c
sort
(partition-by identity)
(filter #(> (count %) 1))
(map first))

How to use frequencies in clojure to combine same frequency and display them once?

I making a poker hands game in clojure. I have to define a function such that such that it returns the ranks in the descending order. For example: order ["2H" "3S" "6C" "5D" "4D"] should return (6 5 4 3 2). But if there is a two-pair like this: ["5H" "AD" "5C" "7D" "AS"] then it should return (14 5 7), but mine returns [14 14 7 5 5], how can I correct this? It should work in the same way for other cases of poker hands as well like for a full house it should give the rank of the three of a kind and the rank of the two of a kind. So, for this I have written:
(defn order
[hand]
"Return a list of the ranks, sorted highest first"
(let [ranks (reverse (sort (map #(.indexOf "--23456789TJQKA" %)
(map #(str (first %)) hand))))]
(if (= ranks [14 5 4 3 2])
[5 4 3 2 1]
(into [] ranks))))
I have also written all the other poker hand functions like flush?, straight? etc.
Also, I have to define another function such that it takes two orders like '(8 5 9) '(8 7 3) and returns true if the first has the larger value of the first difference, else false. Could someone please give me an idea how to do this?
Updated to show sorting by count, then rank:
(defn ->r [hand]
(let [ranks (zipmap "23456789TJKQA" (range 2 15)) ; creates a map like {\2 2, .... \A 14}
count-then-rank
(fn [x y] (compare
[(second y) (first y)]
[(second x) (first x)]))]
(->> hand
(map (comp ranks first)) ; returns the rank for each card eg: '(5 14 5 7 14)
frequencies ; returns a map of rank vs count eg: {5 2, 14 2, 7 1}
(sort count-then-rank) ; becomes a sorted list of tuples eg: '([14 2] [5 2] [7 1])
(map first)))) ; extract the first value each time eg: '(14 5 7)
For a more complete solution, you can use the frequencies to determine if you have 4 of a kind, 3 of a kind, full house etc.
Updated with more info on straight and straight flush:
For a straight, one approach is:
Extract the ranks so you would have a list like '(14 3 2 4 5)
Sort this list to get '(2 3 4 5 14)
Get the first element: 2, and the last element 14
Construct a range from 2 (inclusive) to 15 (exclusive) to get '(2 3 4 5 6 7 8 9 10 11 12 13 14)
Compare against the sorted sequence. In this case the result is false.
Retry, but first replace 14 with 1.
replace => '(1 3 2 4 5)
sort => '(1 2 3 4 5)
(range 1 6) => '(1 2 3 4 5)
This time, the range and the sorted list match, so this is a straight.
(defn straight? [cards] ; eg: ["AH" "3H" "2H" "4H" "5H"]
(let [ranks (zipmap "23456789TJKQA" (range 2 15))
ranks-only (map (comp ranks first) cards) ; eg: '(14 3 2 4 5)
ace-high (sort ranks-only) ; eg: '(2 3 4 5 14)
ace-low (sort (replace {14 1} ranks-only)) ; eg: '(1 2 3 4 5)
consecutive #(= (range (first %) (inc (last %))) %)] ; eg: (= (range 1 6) '(1 2 3 4 5))
(or (consecutive ace-high)
(consecutive ace-low))))
For a flush, simply extract all the suits, and then ensure they are all equal:
(defn flush? [cards]
(apply = (map second cards))) ; this is when suit is the second character
Now, simply combine these two boolean conditions to determine if this is a straight flush
(defn straight-flush? [cards]
(and (straight? cards) (flush? cards)))
See if you can solve 4clojure best hand puzzle, to open up a large number of different ways to tackle this. When I solved this, I used similar, but not identical functions.
Spoiler a more complete solution (using suit first "D7" instead of rank first "7D") is below
https://github.com/toolkit/4clojure-solutions/blob/master/src/puzzle_solutions/best_hand.clj
I think frequencies will get you closer to what you're looking for.
user=> (frequencies [14 14 7 5 5])
{14 2, 7 1, 5 2}
You could use this for sorting:
user=> (sort-by (frequencies [14 14 7 5 5]) [14 14 7 5 5])
(7 14 14 5 5)
And then you could use distinct:
user=> (distinct [14 14 7 5 5])
(14 7 5)
Putting all of these together should get you exactly what you want. I'll leave that as an exercise for the reader. When I'm stuck wondering if there's an easy way to do something, I often turn to Clojure's cheatsheet.

Overhand Shuffle with Clojure - almost

I'm trying to implement a Overhand Shuffle in Clojure as a bit of a learning exercise
So I've got this code...
(defn overhand [cards]
(let [ card_count (count cards)
_new_cards '()
_rand_ceiling (if (> card_count 4) (int (* 0.2 card_count)) 1)]
(take card_count
(reduce into (mapcat
(fn [c]
(-> (inc (rand-int _rand_ceiling))
(take cards)
(cons _new_cards)))
cards)))))
It is very close to doing what I want, but it is repeatedly taking the first (random) N number of cards off the front, but I want it to progress through the list...
calling as
(overhand [1 2 3 4 5 6 7 8 9])
instead of ending up with
(1 2 3 1 2 1 2 3 4)
I want to end up with
(7 8 9 5 6 1 2 3 4)
Also, as a side note this feels like a really ugly way to indent/organize this function, is there a more obvious way?
this function is creating a list of lists, transforming each of them, and cating them back together. the problem it that it is pulling from the same thing every time and appending to a fixed value. essentially it is running the same operation every time and so it is repeating the output over with out progressing thgough the list. If you break the problem down differently and split the creation of random sized chunks from the stringing them together it gets a bit easier to see how to make it work correctly.
some ways to split the sequence:
(defn random-partitions [cards]
(let [card_count (count cards)
rand_ceiling (if (> card_count 4) (inc (int (* 0.2 card_count))) 1)]
(partition-by (ƒ [_](= 0 (rand-int rand_ceiling))) cards)))
to keep the partitions less than length four
(defn random-partitions [cards]
(let [[h t] (split-at (inc (rand-int 4)) cards)]
(when (not-empty h) (lazy-seq (cons h (random-partition t))))))
or to keep the partitions at the sizes in your original question
(defn random-partitions [cards]
(let [card_count (count cards)
rand_ceiling (if (> card_count 4) (inc (int (* 0.2 card_count))) 1)
[h t] (split-at (inc (rand-int rand_ceiling)) cards)]
(when (not-empty h) (lazy-seq (cons h (random-partition t))))))
(random-partitions [1 2 3 4 5 6 7 8 9 10])
((1 2 3 4) (5) (6 7 8 9) (10))
this can also be written without directly using lazy-seq:
(defn random-partitions [cards]
(->> [[] cards]
(iterate
(ƒ [[h t]]
(split-at (inc (rand-int 4)) t)))
rest ;iterate returns its input as the first argument, drop it.
(map first)
(take-while not-empty)))
which can then be reduced back into a single sequence:
(reduce into (random-partitions [1 2 3 4 5 6 7 8 9 10]))
(10 9 8 7 6 5 4 3 1 2)
if you reverse the arguments to into it looks like a much better shuffle
(reduce #(into %2 %1) (random-partitions [1 2 3 4 5 6 7 8 9 10]))
(8 7 1 2 3 4 5 6 9 10)
Answering your indentation question, you could refactor your function. For instance, pull out the lambda expression from mapcat, defn it, then use its name in the call to mapcat. You'll not only help with the indentation, but your mapcat will be clearer.
For instance, here's your original program, refactored. Please note that issues with your program have not been corrected, I'm just showing an example of refactoring to improve the layout:
(defn overhand [cards]
(let [ card_count (count cards)
_new_cards '()
_rand_ceiling (if (> card_count 4) (int (* 0.2 card_count)) 1)]
(defn f [c]
(-> (inc (rand-int _rand_ceiling))
(take cards)
(cons _new_cards)))
(take card_count (reduce into (mapcat f cards)))))
You can apply these principles to your fixed code.
A great deal of indentation issues can be resolved by simply factoring out complex expressions. It also helps readability in general.
A better way to organise the function is to separate the shuffling action from the random selection of splitting points that drive it. Then we can test the shuffler with predictable splitters.
The shuffling action can be expressed as
(defn shuffle [deck splitter]
(if (empty? deck)
()
(let [[taken left] (split-at (splitter (count deck)) deck)]
(concat (shuffle left splitter) taken))))
where
deck is the sequence to be shuffled
splitter is a function that chooses where to split deck, given its
size.
We can test shuffle for some simple splitters:
=> (shuffle (range 10) (constantly 3))
(9 6 7 8 3 4 5 0 1 2)
=> (shuffle (range 10) (constantly 2))
(8 9 6 7 4 5 2 3 0 1)
=> (shuffle (range 10) (constantly 1))
(9 8 7 6 5 4 3 2 1 0)
It works.
Now let's look at the way you choose your splitting point. We can illustrate your choice of _rand_ceiling thus:
=> (map
(fn [card_count] (if (> card_count 4) (int (* 0.2 card_count)) 1))
(range 20))
(1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3)
This implies that you will take just one or two cards from any deck of less than ten. By the way, a simpler way to express the function is
(fn [card_count] (max (quot card_count 5) 1))
So we can express your splitter function as
(fn [card_count] (inc (rand-int (max (quot card_count 5) 1))))
So the shuffler we want is
(defn overhand [deck]
(let [splitter (fn [card_count] (inc (rand-int (max (quot card_count 5) 1))))]
(shuffle deck splitter)))