2014-10-07 3 views
4

Я хотел бы взять число, 20 и список. '(1 2 3 4 5 6 7 8 9 10) и вернуть коллекцию, содержащую два значения для каждого значения в исходном списке: исходное значение в паре с остатком при погружении 20 на это значение. Было бы неплохо, если бы исходные значения каким-то образом были привязаны к остаткам, чтобы я мог легко получить каждое число, которое создало конкретный остаток. В основном я хочу какую-то функцию func:Clojure: сложная итерация через список?

user=> (func 20 '(1 2 3 4 5 6 7 8 9 10)) 
'(:0 1, :0 2, :2 3,... :20 0) 

Однако я имея невероятно трудное время просто выяснить, как итерацию по списку. Может ли кто-нибудь помочь мне понять, как использовать элементы списка самостоятельно, а затем как вернуть элемент, который был разделен на 20, и если он возвращает остаток?

Моя мысль заключалась в том, чтобы использовать что-то подобное в программе, которая вычисляет квадратные корни. Если номера были закреплены остатком, то я мог бы запросить коллекцию, чтобы получить все числа, которые делят вход с остатком 0.


Здесь был мой предварительным способом идти об этом.

;; My idea on the best way to find a square root is simple. 
;; If I want to find the square root of n, divide n in half 
;; Then divide our initial number (n) by all numbers in the range 0...n/2 
;; Separate out a list of results that only only return a remainder of 0. 
;; Then test the results in a comparison to see if the elements of our returned 
;; list when squared are equal with the number we want to find a square root of. 
;; First I'll develop a function that works with evens and then odds 

(defn sqroot-range-high-end [input] (/ input 2)) 
(sqroot-range-high-end 36) ; 18 

(defn make-sqrt-range [input] (range (sqroot-range-high-end (+ 1 input)))) 
(make-sqrt-range 36) ; '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18) 

(defn zero-culler [input] (lazy-seq (remove zero? (make-sqrt-range input)))) 
(zero-culler 100) ; '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18) 

(defn odd-culler [input] (lazy-seq (remove odd? (zero-culler input)))) 
(odd-culler 100) ; '(2 4 6 8 10 12 14 16 18) 

;;the following is where I got stuck 
;;I'm new to clojure and programming, 
;;and am just trying to learn in a way that I understand 

(defn remainder-culler [input] 
    (if 
    (/ input (first odd-culler (input))) 
    input) 
    (recur (lazy-seq (input))) 
) 

(remainder-culler 100) 
+2

Вам не нужно обертывать удаление в lazy-seq, он уже ленив. –

ответ

5

Добро пожаловать в Clojure !

Быстрое примечание: [1 2 3 4 5 6 7 8 9 10] - это вектор, а не список.

Когда вы говорите «с ключом», это заставляет меня думать, что вы ищете что-то, что возвращает карту.

Карты

Это где Clojure's cheatsheet приходит очень удобно. Вы пытаетесь создать карту из функции. Карта - это своего рода коллекция, поэтому, если вы перейдете в раздел «Коллекции» на китчете и прокрутите вниз до карт, вы увидите несколько категорий. Вы хотите создать его, поэтому посмотрите в этом списке и изучите ссылки на документацию Clojure.

Это приведет вас к очень удобной функции group-by. Вы даете ему функцию и коллекцию, и она возвращает карту, содержащую все элементы из этой коллекции, связанные с результатом применения f к каждому значению.

> (group-by #(rem 20 %) [1 2 3 4 5 6 7 8 9 10]) 
{0 [1 2 4 5 10], 2 [3 6 9], 6 [7], 4 [8]} 

Если вы хотите, чтобы ключи быть фактическими ключевыми словами, вы должны изменить анонимную функцию, чтобы вернуть ключевые слова:

> (group-by #(keyword (str (rem 20 %))) [1 2 3 4 5 6 7 8 9 10]) 
{:0 [1 2 4 5 10], :2 [3 6 9], :6 [7], :4 [8]} 

Обратите внимание, что возвращаемые значения в векторах. Это связано с тем, что вы не можете сопоставить два элемента из одной клавиши (т. Е. Функции кодирования карт).

Итерация/Список Постижение

Теперь все, что сказал, я не уверен, что это то, что вы ищете. Вы спросили: «Может ли кто-нибудь помочь мне понять, как использовать элементы списка самостоятельно, а затем как вернуть элемент, который был разделен на 20, и если он возвращает остаток?» Это звучит для меня как случай для for. Для ваших целей вы можете думать об этом как об итерации, но это действительно делает listcomprehension.

(for [i [1 2 3 4 5 6 7 8 9 10]] 
    (list (rem 20 i) i)) 

Еще раз, если вы действительно хотите использовать ключевые слова вместо значений, то вы можете сделать:

(for [i [1 2 3 4 5 6 7 8 9 10]] 
    (list (keyword (str (rem 20 i))) i)) 

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

(map #(list (keyword (str (rem 20 %))) 
      %) 
    [1 2 3 4 5 6 7 8 9 10]) 

Если вам не нравятся вложенные структуры, которые они возвращают, вы можете использовать flatten.

Фильтр

Но я все еще не уверен, что вы хотите, чтобы это решить вашу проблему. В ваших комментариях у вас есть «Отделите список результатов, которые только возвращают остаток 0.» Это звучит для меня как случай для filter, который в качестве побочного преимущества ленив.

> (filter #(zero? (rem 20 %)) [1 2 3 4 5 6 7 8 9 10]) 
(1 2 4 5 10) 

Ta-da. Он просто выплескивает элементы оригинальной коллекции, которые отвечают вашим потребностям.

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

+0

Спасибо за это, это было очень полезно. – dmbennett

2
(map #(vector (rem 20 %) %) (range 1 21)) 
;; => ([0 1] [0 2] [2 3] ... [1 19] [0 20]) 

Однако я имея невероятно трудное время просто выяснить, как итерацию по списку

Для итерацию, используйте функцию высшего порядка, как clojure.core/map

возвращает элемент, который был разделен на 20, и если он возвращает остаток

Вы хотите вернуть 2 вещи. fn, поставляемый в clojure.core/map, может сделать это, возвратив вектор двух элементов.

Для определения остатка, используйте rem

ключ к остатку возвращается

Поскольку может быть несколько операций, возвращающихся тем же остаток, мы не можем создать карту ({}) и использование остаток в качестве ключа - будут столкновения.

clojure.core/range Используется для создания коллекции знаменателей.

Чтобы быть менее явной и использовать clojure.core/juxt следующее выполняет то же самое:

(map (juxt (partial rem 20) identity) (range 1 21)) 

И, наконец, спараметрировать всю эту вещь и вернуть ленивую последовательность:

(defn rem-denominator 
    [n] 
    (map (juxt (partial rem n) identity) 
     (iterate inc 1))) 

(take 5 (rem-denominator 20)) 
;; => ([0 1] [0 2] [2 3] [0 4] [0 5]) 
(take 20 (rem-denominator 20)) 
;; => ([0 1] [0 2] [2 3] ... [1 19] [0 20]) 
+0

Очень хороший код, но вы можете написать объяснение. – TheBat

Смежные вопросы