Лучший способ переслать заполнить ноль значений в списке Clojure - PullRequest
2 голосов
/ 06 ноября 2019

Я написал функцию для перенаправления заполнения нулевых значений в заданном списке. Функция работает, как и ожидалось, но мне было интересно, есть ли лучший (читай идиоматичнее) способ достижения этого в clojure.

Под прямым заполнением нулевых значений я имею в виду: распространение последнего не нулевого элемента вперед доследующий ненулевой элемент.

Функция

(defn ffill [mylist first-value last-value]
  (let [mylist-0 (concat (list first-value) mylist)
        mylist-N (concat mylist-0 (list (dec (count mylist-0))))
        mylist-idx (map-indexed (fn [i val] (if (not (nil? val)) i nil)) mylist-N)
        mylist-idx-no-nils (filter #(->> % (nil?) (not)) mylist-idx)
        ffidx (flatten (map #(repeat (- %2 %1) %1) mylist-idx-no-nils (next mylist-idx-no-nils)))]
    (map #(nth mylist-N %) (next ffidx))
     ))

Примеры

(ffill '(nil nil nil "a" "b" "c" nil "d" nil) "a" "d")
("a" "a" "a" "a" "b" "c" "c" "d" "d")

(ffill '("z" nil nil "a" "b" "c" nil "d" nil) "a" "d")
("z" "z" "z" "a" "b" "c" "c" "d" "d")

(ffill '("z" nil nil "a" "b" "c" nil "e") "a" "d")
("z" "z" "z" "a" "b" "c" "c" "e")

(ffill '(0 nil nil nil 4 5 nil nil 8 nil) 0 8)
(0 0 0 0 4 5 5 5 8 8)

(ffill '(0 nil nil nil 4 5 nil nil 8) 0 8)
(0 0 0 0 4 5 5 5 8)

(ffill '(nil nil nil nil 4 5 nil nil 8 nil) 0 8)
(0 0 0 0 4 5 5 5 8 8)

Ответы [ 4 ]

3 голосов
/ 06 ноября 2019

Из того, что я вижу, последний параметр вашей функции не используется. Учитывая это, следующее должно работать. Если я что-то неправильно понял относительно параметра, пожалуйста, дайте мне знать.

(defn propagate-non-nil-values [s begin]
  (when (seq s)
    (if (nil? (first s))
      (cons begin (propagate-non-nil-values (rest s) begin))
      (cons (first s) (propagate-non-nil-values (rest s) (first s))))))
2 голосов
/ 06 ноября 2019

Использование reductions:

(defn fwd-fill [x xs]
 (rest
  (map first
   (reductions 
     (fn [[s p] x] [(or x p s) x])
     [nil x]
     xs))))  

(fwd-fill 0 '(nil nil nil nil 4 5 nil nil 8 nil))
;==> (0 0 0 0 4 5 5 5 8 8)
2 голосов
/ 06 ноября 2019

Вы можете использовать lazy-seq:

(defn left-fill-lazy [init coll]
  (when (seq coll)
    (lazy-seq
      (let [v (first coll)
            n (if (some? v) v init)]
        (cons n (left-fill-lazy n (rest coll)))))))

(left-fill-lazy "a" '(nil nil "a" nil "c" nil "d" nil))
=> ("a" "a" "a" "a" "c" "c" "d" "d")

Вы можете использовать датчик, который отслеживает самое последнее значение, отличное от nil:

(defn left-fill
  ([] (left-fill nil))
  ([init]
   (fn [rf]
     (let [p (volatile! init)]
       (fn
         ([] (rf))
         ([result] (rf result))
         ([result input]
          (if (some? input)
            (do (vreset! p input)
                (rf result input))
            (rf result @p))))))))

(sequence (left-fill "a") '(nil nil "a" nil "c" nil "d" nil))
=> ("a" "a" "a" "a" "c" "c" "d" "d")

Вы можете (ab) использовать молниидля того же эффекта:

(defn left-fill-zip [l]
  (loop [loc (z/seq-zip l)]
    (if (z/end? loc)
      (z/root loc)
      (recur
        (z/next
          (cond
            (some? (z/node loc)) loc
            (z/left loc) (z/replace loc (z/node (z/left loc)))
            :else loc))))))

(left-fill-zip '("z" nil nil ("a" "b" ("c" nil) "d" nil)))
=> ("z" "z" "z" ("a" "b" ("c" "c") "d" "d"))
1 голос
/ 08 ноября 2019

Вот моя версия, похожая на другие выше:

(defn ffill [xs default]
  (when (seq xs)
    (let [x           (first xs)
          new-default (if (nil? x) default x)]
      (cons new-default
            (lazy-seq (ffill (rest xs) new-default))))))

;; (ffill '(nil nil nil "a" "b" "c" nil "d" nil) "a")
;; => ("a" "a" "a" "a" "b" "c" "c" "d" "d")

;; (ffill '("z" nil nil "a" "b" "c" nil "d" nil) "a")
;; => ("z" "z" "z" "a" "b" "c" "c" "d" "d")

;; (ffill '("z" nil nil "a" "b" "c" nil "e") "a")
;; => ("z" "z" "z" "a" "b" "c" "c" "e")

;; (ffill '(0 nil nil nil 4 5 nil nil 8 nil) 0)
;; => (0 0 0 0 4 5 5 5 8 8)

;; (ffill '(0 nil nil nil 4 5 nil nil 8) 0)
;; => (0 0 0 0 4 5 5 5 8)

;; (ffill '(nil nil nil nil 4 5 nil nil 8 nil) 0)
;; => (0 0 0 0 4 5 5 5 8 8)

;; (ffill '(nil true nil false nil) 1)
;; => (1 true true false false)
  • when заботится о конце последовательности xs
  • new-default определяетсябыть заголовком текущей части списка или предоставленной по умолчанию для этой итерации
  • lazy-seq, поскольку список не должен быть конечным

РЕДАКТИРОВАТЬ: Мой предыдущийРешение использовало or, что было неверно, поскольку оно заменило бы false значения, как если бы они были nil, поэтому я обновил свой ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...