2010-10-13 3 views
4

Je suis en train de trouver un moyen Clojure-idiomatiques à « compresser » un vecteur:Clojure compresser vecteur

(shift-nils-left [:a :b :c :a nil :d nil]) 
;=> (true [nil nil :a :b :c :a :d]) 
(shift-nils-left [nil :a]) 
;=> (false [nil :a]) 
(shift-nils-left [:a nil]) 
;=> (true [nil :a]) 
(shift-nils-left [:a :b]) 
;=> (false [:a :b]) 

En d'autres termes, je veux passer à l'extrémité gauche du vecteur toutes les nil valeurs , sans changer la longueur. Le booléen indique si un décalage s'est produit. La structure "extérieure" peut être seq, mais le résultat à l'intérieur devrait être un vecteur.

Je soupçonne que la fonction implique filter (sur les valeurs nulles) et into à ajouter à un vecteur de nil s de la même longueur que l'original, mais je ne suis pas sûr de savoir comment réduire le résultat à la longueur d'origine. Je sais comment cette "main longue", mais je soupçonne que Clojure sera en mesure de le faire dans une seule ligne. Je rêve à l'idée d'écrire un Bejeweled comme exercice pour apprendre Clojure.

Merci.

+0

Si seulement vous pouviez déplacer les valeurs vers la droite à la place ... –

+0

La structure de données que j'ai en tête est un vecteur ou 8 vecteurs. Chaque vecteur interne représente une colonne de bijoux. La fonction 'apply-move' remplacera les joyaux qui disparaissent avec les valeurs' nil'. Je vais ensuite utiliser la fonction "compress" pour déplacer les valeurs "nil" vers le haut et ensuite remplir avec des bijoux (j'ai déjà une fonction pour le faire). – Ralph

Répondre

2

Je voudrais écrire comme ceci:

(ns ... 
    (:require [clojure.contrib.seq-utils :as seq-utils])) 

(defn compress-vec 
    "Returns a list containing a boolean value indicating whether the 
    vector was changed, and a vector with all the nils in the given 
    vector shifted to the beginning." 
    ([v] 
    (let [shifted (vec (apply concat (seq-utils/separate nil? v)))] 
     (list (not= v shifted) 
      shifted)))) 

Edit: ainsi, la même chose que Thomas m'a battu à l'affichage, mais je ne voudrais pas utiliser aplatir juste au cas où vous finissez par utiliser une sorte d'objet seqable pour représenter les bijoux.

+0

Je pense utiliser un vecteur de vecteurs pour le tableau. Chaque vecteur interne représentera une colonne (pour que les bijoux puissent tomber). Je vais remplacer les bijoux enlevés par des nils, puis utiliser la fonction shift-nils-left pour les "percoler" en haut de la colonne. Je remplacerai ensuite chaque néant par un nouveau bijou aléatoire. Les bijoux eux-mêmes seront probablement: rouge,: blanc, etc. – Ralph

+0

BTW, j'aurais dû savoir que quelqu'un de la communauté Clojure aurait pensé au problème de partition avant moi :-) – Ralph

2

Peut-être cette façon:

(defn shift-nils-left 
    "separate nil values" 
    [s] 
    (let [s1 (vec (flatten (clojure.contrib.seq/separate nil? s)))] 
     (list (not (= s s1)) s1))) 
+0

J'aurais dû utiliser 'not =', comme le fait Dreish. ;-) – Thomas

2

Un peu plus d'approche de bas niveau. Il traverse l'entrée seq une seule fois ainsi que le vecteur des non-nils une fois. Les deux approches de niveau supérieur traversent la séquence d'entrée deux fois (pour nil? et (complenent nil?)). Le not= parcourt l'entrée une troisième fois dans le pire des cas sans décalage.

(defn compress-vec 
    [v] 
    (let [[shift? nils non-nils] 
     (reduce (fn [[shift? nils non-nils] x] 
        (if (nil? x) 
        [(pos? (count non-nils)) (conj nils nil) non-nils] 
        [shift? nils (conj non-nils x)])) 
       [false [] []] v)] 
    [shift? (into nils non-nils)])) 
1
(def v [1 2 nil 4 5 nil 7 8]) 

(apply vector (take 8 (concat (filter identity v) (repeat nil)))) 

Ceci crée une séquence de valeurs non nulles dans le vecteur en utilisant filter et ajoute ensuite Nils à la fin de la séquence. Cela donne les valeurs que vous voulez en tant que séquence, puis les convertit en un vecteur. Le take 8 garantit que la taille du vecteur est correcte.