代码之家  ›  专栏  ›  技术社区  ›  Roskoto

clojure和python中的惰性无限序列

  •  15
  • Roskoto  · 技术社区  · 15 年前

    下面是我在clojure和python中为费波纳契数的无限长懒惰序列找到的最佳实现:

    Clojure公司:

    (def fib-seq (lazy-cat [0 1]
     (map + fib-seq (rest fib-seq))))
    

    样品使用情况:

     (take 5 fib-seq)
    

    蟒蛇:

    def fib():
     a = b = 1
     while True:
      yield a
      a,b = b,a+b
    

    样品使用情况:

    for i in fib():
      if i > 100:
        break
      else:
        print i
    

    显然,python代码更直观。

    我的问题是:Clojure中是否有更好(更直观和简单)的实现?

    编辑

    我将在打开后续问题 Clojure Prime Numbers

    9 回复  |  直到 11 年前
        1
  •  14
  •   John Lawrence Aspden    15 年前

    我喜欢:

    (def fibs 
      (map first 
           (iterate 
               (fn [[ a, b       ]]  
                    [ b, (+ a b) ]) 
               [0, 1])))     
    

    这似乎与Python/Generator版本有一些相似之处。

        2
  •  36
  •   flash42    11 年前

    我同意帕维尔的观点,直觉是主观的。因为我(慢慢地)开始摸索哈斯克尔,我可以分辨出Clojure代码的作用,尽管我一生中从未写过Clojure一行。所以我会认为Clojure系列相当直观,因为我以前见过它,我正在适应一种更实用的思维方式。

    让我们考虑一下数学定义,好吗?

           { 0                   if x = 0 }
    F(x) = { 1                   if x = 1 }
           { F(x - 1) + F(x - 2) if x > 1 }
    

    这是不理想的,格式方面的-这三个括号排成一个大括号-但谁在数?对于大多数数学背景的人来说,这是一个非常清楚的斐波那契数列定义。让我们来看看哈斯克尔的相同之处,因为我比Clojure更清楚:

    fib 0 = 0
    fib 1 = 1
    fib n = fibs (n - 1) + fibs (n - 2)
    

    这是一个函数, fib ,返回第n个斐波那契数。不完全是我们在python或clojure中所拥有的,所以让我们来解决这个问题:

    fibs = map fib [0..]
    

    这使得 fibs 斐波那契数的无限列表。 fibs !! 1 是1, fibs !! 2 是1, fibs !! 10 是55,依此类推。然而,这可能是非常低效的,即使在一种依赖于高度优化的递归(如haskell)的语言中也是如此。让我们看看哈斯克尔的clojure定义:

    fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
    

    前两个字符非常简单: 0 : 1 : 用元素0和1创建一个列表,然后再创建一些。但剩下的是什么?好, 光纤 是我们已经得到的清单,而且 tail fibs 调用 tail 到目前为止,我们列表中的函数,它返回从第2个元素开始的列表(类似于python saying中的 fibs[1:] )所以我们把这两个清单- 小谎 尾纤 -我们把它们和 + 函数(运算符)-也就是说,我们添加每个元素的匹配元素。让我们看看:

    fibs       = 0 : 1 : ...
    tail fibs  = 1 : ...
    zip result = 1 : ...
    

    所以我们的下一个元素是1!但是我们把它加回到我们的 小谎 列出,看看我们得到了什么:

    fibs       = 0 : 1 : 1 : ...
    tail fibs  = 1 : 1 : ...
    zip result = 1 : 2 : ...
    

    我们这里有一个 递归列表定义 .当我们添加更多元素到 小谎 和我们一起 zipWith (+) fibs (tail fibs) 当我们添加元素时,更多的元素会变得可用。请注意,haskell在默认情况下是懒惰的,所以只制作一个这样的无限列表不会破坏任何东西(只是不要尝试打印它)。

    因此,虽然理论上这可能与我们之前的数学定义相同,但它将结果保存在 小谎 列出(有点像自动记忆),我们很少会遇到在简单的解决方案中可能遇到的问题。为了完整性,让我们定义 小谎 根据我们的新功能 小谎 名单:

    fib n = fibs !! n
    

    如果我还没有失去你,那很好,因为这意味着你理解法律法规。看:

    (def fib-seq (lazy-cat [0 1]
     (map + fib-seq (rest fib-seq))))
    

    我们列了个清单, fib-seq . 它从两个元素开始, [0 1] 就像哈斯克尔的例子。我们将这两个初始元素的延迟连接 (map + fib-seq (rest fib-seq)) -假设 rest 做同样的事 在haskell中,我们只是在较低的偏移量处将列表与自身进行组合,然后将这两个列表与 + 操作员/功能。

    在你的头脑中思考了几次,并探索了其他一些例子之后,这种生成斐波那契级数的方法至少变得半直观了。它至少有足够的直觉让我用一种我不懂的语言来识别它。

        3
  •  12
  •   Georg Schölly Crazy Developer    15 年前

    如果你不知道任何命令式语言,这对你来说是直观的吗?

    a = a + 5
    

    世界跆拳道联盟? a 清晰地 不是和 a + 5 .

    如果 a = a + 5 a + 5 = a ?

    为什么这个不行????

    if (a = 5) { // should be == in most programming languages
        // do something
    }
    

    有很多事情是不清楚的,除非你以前在别的地方见过它,并且了解它的目的。很长一段时间我都不知道 yield 关键字,实际上我不知道它做了什么。

    你认为命令式方法更易读,因为你已经习惯了。

        4
  •  6
  •   Brian Carper    15 年前

    Clojure代码对我来说是直观的(因为我知道Clojure)。如果你想要一些看起来更像你熟悉的东西,你可以试试这个,一个高效的、过于冗长的递归版本:

    (use 'clojure.contrib.def)   ; SO's syntax-highlighting still sucks
    (defn-memo fib [n]
      (cond (= n 0) 0
            (= n 1) 1
            :else   (+ (fib (- n 1))
                       (fib (- n 2)))))
    
    (def natural-numbers (iterate inc 0))
    
    (def all-fibs
      (for [n natural-numbers]
        (fib n)))
    

    但是对于不知道递归或记忆是什么的人来说,这也不是直觉。对于大多数程序员来说,“无限的懒惰序列”的想法可能并不直观。我猜不出你的大脑里有什么,所以我不知道对你来说更直观的clojure函数会是什么样子,而不是“看起来更像python”。

    对于一个不懂编程的人来说,所有这些东西看起来都像胡说八道。什么是循环?什么是函数?这是什么 yield 事情?这就是我们开始的地方。直觉是你迄今为止所学知识的一个功能。非直观代码是您还不熟悉的代码。从“我知道这个”到“它本质上更直观”的推断是错误的。

        5
  •  5
  •   Timothy Pratley    15 年前

    这个 wiki 对Clojure中的各种Fibonacci实现进行了深入的处理,如果您还没有看到它,可能会感兴趣。

        6
  •  2
  •   Svante    15 年前

    你应该总是使用适合问题的语言 * . 您的python示例的级别比clojure示例低——对于初学者来说更容易理解,但是对于了解适合更高级别概念的人来说,编写和解析更为繁琐。

    * 顺便说一下,这也意味着拥有一种你能适应的语言总是很好的。

        7
  •  2
  •   user206993    15 年前

    这里有一个解决方案。

    (defn fib-seq [a b]
      (cons (+ a b) (lazy-seq (fib-seq (+ a b) a))))
    
    (def fibs (concat [1 1] (fib-seq 1 1)))
    
    user=> (take 10 fibs)
    (1 1 2 3 5 8 13 21 34 55)
    
        8
  •  -1
  •   Tadeusz A. Kadłubowski    15 年前

    想想看,你会怎么写《懒猫》和《克洛尤里的重现》。

        9
  •  -5
  •   nilamo    15 年前
    (take 5 fibs)
    

    似乎是最直观的。我是说,这正是你要做的。为了知道应该发生什么,你甚至不需要了解任何关于语言的事情,甚至不需要知道那是什么语言。

    推荐文章