以下函数式编程模式的正确术语是什么?

时间:2010-08-16 12:24:44

标签: functional-programming clojure lisp terminology sequence

我听说它被称为 stream infinite list,有时甚至是 {{ 3}}

以下模式的正确用语是什么? (显示的是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)

2 个答案:

答案 0 :(得分:14)

简短回答:stream-builder返回一个返回无限序列/列表的函数,必须对其进行“懒惰”评估(因为你无法在有限时间内无限长地评估某些东西)。在Clojure世界中,您可能不应该在示例“流”中调用任何内容,以避免与其他概念混淆。

更长的答案:

编程语言中思维多样性的一个不幸的副作用是我们经常使用相同的词来表达不同的含义。你提到的所有三个单词(“Stream”,“无限列表”,“懒惰序列”)都是以串行方式引用处理元素,而在Clojure中我们称之为“序列”。但是,每个单词所隐含的细微差别都略有不同。 / p>

“流”通常指某些元素序列,并且现在经常在有限字符序列的上下文中使用。这些字符序列通常来自文件,网络源或Unix管道。

如果序列以具有无限数量元素的方式定义,我们可以将其称为无限序列。通常无限序列在内部表示为linked list,因此我们可以将这些称为“无限列表”。虽然,老实说,我更倾向于在Clojure社区中听到“无限序列”这个术语,所以我们并不依赖于特定的实现。

最后,Clojure中“懒惰序列”的细微差别是指对“按需”发生的数据结构的顺序评估模式。换句话说,这里的重点是评估的 lazy 性质;在您要求之前,实际上不会计算序列中特定元素的值。

总之,在Clojure中你应该使用:

  • “list”以引用具有链接列表实现的内容
  • “懒惰”指的是按需评估的内容
  • “无限”指的是不是有限大小的序列(因此必须是懒惰的)
  • “stream”表示来自外部源的管道(字符)序列

答案 1 :(得分:10)

这不是你问题的答案,但为了编写“好”的clojure代码,我想用你的例子指出一些事情。

功能风格的一个好处是能够一起组合功能。但是如果你看一下你编写的函数,如果不依赖于其他地方提供的功能,它们就不会做太多。

例如,stream-builder只返回n的两元素列表和一个处理伪递归的匿名函数。

我的猜测是你试图寻找一个懒惰的序列,但这样做需要支持函数,这些函数知道实现细节以实现下一个元素,即stream和{{1} }。谢天谢地,你可以按照以下方式完成它:

(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)))))

以上实际上将返回无限的值序列,因此我们需要提取second$内绑定的限制行为:

(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)))))))

以上将懒洋洋地返回stream的{​​{1}}元素:

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

最后,每个函数都做得很好,并且可以与其他函数组合:

最后,您将n定义为奇数整数的惰性序列。危险在于序列将在实现时保持不变(这被称为“保持在序列中的头部”)。这可能会不必要地占用多余的内存来保留所有已实现的元素,并防止垃圾回收。

要解决这个问题,要么不要对延迟序列进行定义,要么使用限制来定义,例如:

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

为了将来参考,我们刚刚编写的内容在核心库中分别以collodd提供:

user=> (take 5 (iterate inc 1))
(1 2 3 4 5)