Несколько предложений:
- Используйте функцию с возвращаемым значением вместо того, чтобы полагаться на побочный эффект (
println
и def
внутри функции), чтобы получить свой результат - Использование
def
только для переменной верхнего уровня, используйте let
для любой временной переменной внутри функции - Создайте несколько функций специального назначения (например, функция для анализа даты; функция для преобразования списка наблюдений в даты), затем используйтеэти функции для создания вашего решения
- Использование макроса потока (например,
->
->>
) для улучшения читабельности
Возможное решение:
(def fmt
"default date formatter"
(f/formatter "dd MM yyyy"))
(def ->date
"utility function to convert string to date"
(partial f/parse fmt))
(->date "01 02 2012")
;; => #object[org.joda.time.DateTime 0x6e880ccd "2012-02-01T00:00:00.000Z"]
(defn ->observations
[intervals]
(->> intervals
(map (fn [{:keys [start_ end_]}]
{:start (->date start_)
:end (->date end_)}))))
(->observations '({:start_ "20 01 2012" :end_ "20 02 2012"}
{:start_ "20 02 2012" :end_ "20 03 2012"}))
;; => ({:start #object[org.joda.time.DateTime 0x4eb450bd "2012-01-20T00:00:00.000Z"], :end #object[org.joda.time.DateTime 0x558bd20f "2012-02-20T00:00:00.000Z"]} {:start #object[org.joda.time.DateTime 0x4009d145 "2012-02-20T00:00:00.000Z"], :end #object[org.joda.time.DateTime 0x42e32d6 "2012-03-20T00:00:00.000Z"]})
(defn mpr_ratio
[start_date end_date intervals]
(let [intrvrl (t/interval start_date end_date)
obsrv-prd (t/in-days intrvrl)]
(->> (map t/interval (map :start intervals) (map :end intervals))
(map (partial t/overlap intrvrl))
(map t/in-days)
(map #(-> %
(/ obsrv-prd)
(* 100.0)))
(reduce +))))
(mpr_ratio (->date "01 02 2012")
(->date "01 12 2012")
(->observations '({:start_ "20 01 2012" :end_ "20 02 2012"}
{:start_ "20 02 2012" :end_ "20 03 2012"}
{:start_ "20 04 2012" :end_ "20 05 2012"}
{:start_ "20 06 2012" :end_ "20 07 2012"})))
;; => 35.526315789473685
UPDATE - функция полезности PDC
(defn covered [state interval]
(if (some #(t/overlaps? interval %) state)
(->> state
(map #(if (t/overlaps? interval %)
(t/interval (t/min-date (t/start %) (t/start interval))
(t/max-date (t/end %) (t/end interval)))
%))
(into (empty state)))
(conj state interval)))
(covered #{} (t/interval (->date "01 02 2012") (->date "05 02 2012")))
;; => #{#object[org.joda.time.Interval 0x30addc0b "2012-02-01T00:00:00.000Z/2012-02-05T00:00:00.000Z"]}
(covered *1 (t/interval (->date "04 02 2012") (->date "07 02 2012")))
;; => #{#object[org.joda.time.Interval 0x7f8893c1 "2012-02-01T00:00:00.000Z/2012-02-07T00:00:00.000Z"]}
(covered *1 (t/interval (->date "02 03 2012") (->date "07 03 2012")))
;; => #{#object[org.joda.time.Interval 0x7f8893c1 "2012-02-01T00:00:00.000Z/2012-02-07T00:00:00.000Z"] #object[org.joda.time.Interval 0x67adc8d1 "2012-03-02T00:00:00.000Z/2012-03-07T00:00:00.000Z"]}
(reduce + (map (comp inc t/in-days) *1))
;; => 13
функция pdc в полном объеме: (обратите внимание, что нужно добавить только одну строку)
(defn pdc_ratio
[start_date end_date intervals]
(let [intrvrl (t/interval start_date end_date)
obsrv-prd (t/in-days intrvrl)]
(->> (map t/interval (map :start intervals) (map :end intervals))
(map (partial t/overlap intrvrl))
;; get covered days only
(reduce covered #{})
(map t/in-days)
(map #(-> %
(/ obsrv-prd)
(* 100.0)))
(reduce +))))