Каков правильный термин для следующего шаблона функционального программирования?

Я слышал, что его называют потоком , как бесконечным списком , и иногда даже как ленивая последовательность .

Какой правильный термин для следующего шаблона? (Показан код Clojure)

(def first$ first)

(defn second$ [str]
  (cond
    (empty? str) ()
    true ((first (rest str)))))

(defn stream-builder [next_ n]
  (cons n (cons (fn [] (stream-builder next_ (next_ n))) ())))

(defn stream [str n]
  (cond
    (= 0 n) ()
    true (cons (first$ str) (stream (second$ str) (- n 1)))))

(def odd 
  (stream-builder (fn [n] 
        (+ 2 n))1))

(println (stream odd 23))

> (1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45)

10
задан Community 23 May 2017 в 12:13
поделиться

2 ответа

Краткий ответ: stream-builder возвращает функцию, которая возвращает бесконечную последовательность / список, который должен вычисляться «лениво» (поскольку вы не можете вычислить что-то бесконечно долгое за конечное время). В мире Clojure вам, вероятно, не следует называть ничего из того, что в вашем примере, «потоками», чтобы избежать путаницы с другой концепцией.

Более длинный ответ:

Прискорбным побочным эффектом разнообразия мысли в языках программирования является то, что мы часто используем одни и те же слова для разных значений. Все три упомянутых вами слова («поток», бесконечный список »,« ленивая последовательность ») относятся к последовательным элементам обработки, и в Clojure мы называем их« последовательностями ». Однако нюансы, подразумеваемые каждым из них, немного отличаются.

«Поток» обычно относится к некоторой последовательности элементов и в настоящее время часто используется в контексте конечных последовательностей символов. Часто эти последовательности символов поступают из файла, сетевого источника или конвейера Unix.

Если последовательность является Определенный таким образом, что он имеет бесконечное количество элементов, мы можем назвать это бесконечной последовательностью. Обычно бесконечные последовательности представляются внутри как связанный список , поэтому мы можем назвать эти «бесконечные списки». Хотя, честно говоря, я бы предпочел услышать термин «бесконечная последовательность» в сообществе Clojure, чтобы мы не были привязаны к конкретной реализации.

Наконец, нюанс «ленивой последовательности» в Clojure относится к шаблону последовательной оценки на структура данных, которая создается «по запросу».Другими словами, акцент здесь делается на ленивом характере оценки; значение конкретного элемента в последовательности фактически не вычисляется, пока вы этого не запросите.

Таким образом, в Clojure вы должны использовать слова:

  • «список» для ссылки на что-то с реализацией связанного списка
  • «ленивый» для ссылки на вещи, которые оцениваются по требованию
  • «бесконечно» до относятся к последовательностям, которые не имеют конечного размера (и, следовательно, должны быть ленивыми)
  • «поток» для ссылки на последовательность, подобную конвейеру (символьную) из внешнего источника
14
ответ дан 3 December 2019 в 16:51
поделиться

Это не ответ на ваш вопрос, но в интересах написания "красивого" кода закрытия я хотел бы указать на несколько вещей с помощью вашего примера.

Одним из преимуществ функционального стиля является возможность объединять функции. Но если вы посмотрите на функции, которые вы написали, они по отдельности мало что делают, если не зависят от функциональности, предоставленной где-то еще.

Например, построитель потока просто возвращает двухэлементный список n и анонимную функцию для обработки псевдорекурсии.

Я предполагаю, что вы пытались использовать ленивую последовательность, но для этого требуются функции поддержки, которые знают детали реализации, чтобы реализовать следующий элемент, а именно, поток и ] второй $ . К счастью, вместо этого вы можете сделать это следующим образом:

(defn stream-builder [f x] ; f is idiomatic for a function arg
  (lazy-seq                ; n is idiomatic for a count, so use x instead
    (cons x 
      (stream-builder f (f x)))))

Вышеупомянутое фактически вернет бесконечную последовательность значений, поэтому нам нужно вытащить ограничивающее поведение, связанное внутри потока :

(defn limit [n coll] ; coll is idiomatic for a collection arg
  (lazy-seq          ; flipped order, fns that work on seqs usually take the seq last
    (when (pos? n)
      (when-let [s (seq coll)]
        (cons (first s) 
          (limit (dec n) (next s)))))))

Вышеупомянутое будет лениво возвращает до n элементов из coll :

user=> (limit 5 (stream-builder inc 1))
(1 2 3 4 5)

В конце концов, каждая функция выполняет одну задачу хорошо и ее можно комбинировать с другими функциями:

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

Чтобы решить эту проблему, либо не определяйте ленивую последовательность, либо определите ее с пределом, например:

(def odds (limit 23 (stream-builder #(+ 2 %) 1)))

Для дальнейшего использования то, что мы только что написали, доступно в основной библиотеке как iterate и принимают соответственно:

user=> (take 5 (iterate inc 1))
(1 2 3 4 5)
10
ответ дан 3 December 2019 в 16:51
поделиться