2013-11-22 2 views
1

Я написал этот код для гнезда функции n раз, и я пытаюсь расширить код для обработки теста. После того как тест вернет нуль, цикл остановится. Результатом будет вектор, содержащий элементы, которые тестировали true. Проще всего добавить цикл while в этом случае? Вот пример того, что я написал:неоднократно применяют функцию до тех пор, пока тест больше не даст значение true

(defn nester [a inter f] 
(loop [level inter expr a] 
    (if (= level 0) expr 
    (if (> level 0) (recur (dec level) (f expr)))))) 

Пример ввода будет целое число 2, и я хочу, чтобы гнездиться функцию вкл, пока выход не является большим, чем 6. Выход должен быть [2 3 4 5 6 7].

ответ

3
(defn nester [a inter f test-fn] 
    (loop [level inter 
     expr a] 
    (if (or (zero? level) 
      (nil? (test-fn expr))) 
     expr 
     (recur (dec level) 
      (f expr))))) 

Если вы также принимать ложные (дополнительно к нулю) из испытаний Ф.Н., вы могли бы составить это более лениво:

(defn nester [a inter f test-fn] 
    (->> (iterate f a) 
     (take (inc inter)) 
     (drop-while test-fn) 
     first)) 

EDIT: Выше был дан ответ на ваш первоначальный вопрос. Теперь, когда у вас есть указанных полностью изменил смысл вашего вопроса:

Если вы хотите создать вектор всех итераций функции F над значением п с предикатом р:

(defn nester [f n p] 
    (->> (iterate f n) 
     (take-while p) 
     vec)) 

(nester inc 2 (partial > 8)) ;; predicate "until the output is greater than six" 
          ;; translated to "as long as 8 is greater than 
          ;; the output" 

=> [2 3 4 5 6 7] 
1

К «гнездо» или повторить функцию над значением, Clojure имеет функцию iterate. Например, (iterate inc 2) можно рассматривать как бесконечный ленивый список [2, (inc 2), (inc (inc 2)), (inc (inc (inc 2))) ...] (я использую скобки [], чтобы не обозначать «список» - на самом деле они представляют собой «вектор» в терминах Clojure, но во избежание путаницы с (), который может обозначать список данных или s-выражение, которое должно быть вызовом функции. iterate делает не возвращает вектор). Конечно, вам, вероятно, не нужен бесконечный список, в который входит ленивая часть. Ленточный список даст вам только то, о чем вы просите. Так что если вы спросите в течение первых десяти элементов, это то, что вы получите:

user> (take 10 (iterate inc 2)) 
> (2 3 4 5 6 7 8 9 10 11) 

Конечно, вы могли бы попробовать попросить весь список, но будьте готовы либо перезагрузить REPL, или отправку в отдельный поток, так как этот вызов не будет конца:

user> (iterate inc 2) 
> (2 
    3 
    4 
    5 
    6 
    7 
    8 
    9 
    10 
    11 
    12 
    13 
    14 
    15 
    16 
    17 
    18 
=== Shutting down REPL === 
=== Starting new REPL at C:\Users\Omnomnomri\Clojure\user === 
Clojure 1.5.0 
user> 

Здесь, я использую clooj, и это то, что он выглядит, когда я перезагрузить REPL. В любом случае, это все касательно. Дело в том, что iterate отвечает на суть вашего вопроса. Другая часть, останавливаясь на каком-либо тестовом состоянии, включает take-while. Как вы можете себе представить, take-while много как take, только вместо остановки после некоторого числа элементов, он останавливается на какой-то условия испытания (или в Clojure просторечии предикат):

user> (take-while #(< % 10) (iterate inc 2)) 
> (2 3 4 5 6 7 8 9) 

Обратите внимание, что take-while является эксклюзивным с его предикатным тестом, так что здесь, как только значение завершит тест (менее 10), оно исключает это значение и включает только предыдущие значения в возвращаемом результате. На данный момент, решая свой пример довольно straightfoward:

user> (take-while #(< % 7) (iterate inc 2)) 
> (2 3 4 5 6) 

И если вам это нужно, чтобы быть вектором, обернуть все это в вызове vec:

user> (vec (take-while #(< % 7) (iterate inc 2))) 
> [2 3 4 5 6] 

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

  • Нет связанных вопросов^_^