2011-12-27 14 views
21

Z Clojure, jak znaleźć pierwszy indeks z dodatnią wartością w tym wektorze [-1 0 3 7 9]?Znajdź indeks elementu pasującego do predykatu w Clojure?

wiem można dostać pierwszy wynik czegoś raczej elegancko z first i filter:

(first (filter pos? [-1 0 99 100 101]))

Ten kod zwraca wartość 99. Odpowiedź, którą chcę, to indeks, który jest 2.

+0

Zobacz tutaj: http://stackoverflow.com/questions/4830900/how-do-i-find-the-index-of-an-item-in-a-vector – nimrodm

+0

Moje pytanie jest zupełnie inne. Zmieniłem mój tytuł, aby to odzwierciedlić. –

Odpowiedz

37

Korzystanie keep-indexed można uzyskać ciąg indeksów, dla których orzeczenie jest spełnione:

(defn indices [pred coll] 
    (keep-indexed #(when (pred %2) %1) coll)) 

Dzięki tej prostej czynności będziesz rozwiązać problemu z wyrażeniem

user=> (first (indices pos? [-1 0 99 100 101])) 
2 

zauważyć, że ze względu na leniwość keep-indexed (i indices), cała sekwencja nie musi być zrealizowana, więc nie są wykonywane żadne dodatkowe obliczenia.

+0

bardzo fajnie. właśnie tego szukałem. –

+0

Działa to świetnie. (Kiedy go wykopałem, zauważyłem, że 'keep-indexed' zawiera ** niezerowy ** (który zawiera wyniki" false ") .Nie znam racjonalnych przesłanek.) –

1
(defn pred-idx [pred [idx hist] cur] 
    (if (pred cur) 
    [(inc idx) (conj hist idx)] 
    [(inc idx) hist])) 

(defn idx-filter [pred col] 
    (second (reduce (partial pred-idx pred) [0 []] col))) 

(first (idx-filter pos? [-1 0 99 100 101])) 
2 

Nie jestem pewien, czy to jest lepsze, ale działa. Myślę, że wymusza to ocenę całej sekwencji i jeśli potrzebujesz wszystkich indeksów, które byłyby lepsze. Prawidłową rzeczą jest, aby jakoś zmienić to w leniwą sekwencję, ale skończyłem na wieczór.

+0

Chcę czegoś, co poradzi sobie z moim przykładem. Znajdź indeks pierwszej wartości dodatniej w kolekcji. –

+0

Przepraszam, nie przeczytałem tego wyraźnie. – Bill

+0

moja wina mój tytuł był niejasny –

0

Spróbuj tego:

(defn first-index 
    ([pred coll] (first-index coll pred 0)) 
    ([pred coll idx] 
    (cond (= coll '()) -1 
      (pred (first coll)) idx 
      :else (recur pred (rest coll) (inc idx))))) 

i używać go tak:

(defn is-pos? [x] 
    (> x 0)) 

(first-index is-pos? [-1 0 3 7 9]) 

Zwraca indeks od zera do pierwszego elementu spełniającego predykat (is-pos? w przykładzie), lub -1, jeśli żaden element nie pasuje do predykatu.

+1

Ta funkcja zwróci wartość -1, jeśli zostanie jawnie przekazana 'nil', ponieważ' (reszta x) 'nigdy nie jest zerowe dla żadnego' x'. Powinieneś nazwać 'seq' na kolekcji, zanim przetestujesz ją na zero. Również '[pred coll]' byłaby bardziej przyjazną kolejnością argumentów niż '[coll pred]' - cf. 'map' i' filter', na przykład. – amalloy

+0

działa to (z sugestiami Amalloya), ale wersja korzystająca z indeksu keep jest znacznie prostsza. – Gert

+0

Tam edytowałem go zgodnie z komentarzem @ amalloy. Pochodzę z tła Scheme, to dziwne, że zero!= '() w Clojure –

3
(defn first-pos [x] 
    (loop [arr x n 0] 
    (if (pos? (first arr)) 
    n 
    (recur (next arr) (inc n))))) 

Jest to dobry przykład zastosowania potężnej rekurencji ogonowej w programowaniu funkcjonalnym.

+0

Nie działa z 'NullPointerException', jeśli nie ma żadnych pozytywnych elementów na liście; również OP poprosił o dopasowanie arbitralnego predykatu, nie tylko "pos.' –

1
(first (filter #(not (nil? %)) (map #(when (pos? %1) %2) [-1 1 0 99 100 101] (range)))) 

Mapa może przyjąć jedną lub więcej kolekcji i zwrócić jedną listę, umieścić warunek na mapie i filtrować zero.

Powiązane problemy