Clojure和Python中的惰性无限序列

2024-05-28 18:10:30 发布

您现在位置:Python中文网/ 问答频道 /正文

以下是在Clojure和Python中可以找到的Fibonacci数的无限懒序列的最佳实现:

Clojure公司:

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

示例用法:

 (take 5 fib-seq)

Python:

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


Tags: rest示例map用法def公司序列seq
1条回答
网友
1楼 · 发布于 2024-05-28 18:10:30

我同意帕维尔的观点,直觉是主观的。因为我(慢慢地)开始grok Haskell,我可以知道Clojure代码是做什么的,即使我这辈子从未写过Clojure代码。因此,我认为Clojure系列相当直观,因为我以前见过它,我正在适应一种更具功能性的思维方式。

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

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

这不太理想,从格式上看-这三个括号应该是一个大括号-但是谁在计算呢?对于大多数有数学背景的人来说,这是斐波那契数列的一个非常明确的定义。让我们看看哈斯克尔的相同之处,因为我比克洛朱尔更了解它:

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

这是一个函数fib,它返回第n个斐波那契数。不完全是我们在Python或Clojure中所拥有的,所以让我们修正一下:

fibs = map fib [0..]

这使得fibs成为一个无限的Fibonacci数列表。fibs !! 1是1,fibs !! 2是1,fibs !! 10是55,依此类推。然而,这可能是非常低效的,即使是在一种依赖于高度优化递归的语言中,比如Haskell。让我们看看Haskell中的Clojure定义:

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

前两个字符非常简单:0 : 1 :生成一个包含元素0和1的列表,然后是更多的元素。但剩下的是什么?好吧,fibs是我们已经得到的列表,到目前为止,tail fibs调用列表中的tail函数,它返回从第二个元素开始的列表(类似于Python中说的fibs[1:])。所以我们把这两个列表-fibstail fibs-和+函数(操作符)压缩在一起,也就是说,我们添加每个列表的匹配元素。让我们看看:

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

所以我们的下一个元素是1!但是,然后我们将其添加回我们的fibs列表中,看看我们得到了什么:

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

这里有一个递归列表定义。当我们使用zipWith (+) fibs (tail fibs)位在fibs结尾添加更多元素时,在添加元素时可以使用更多元素。注意Haskell在默认情况下是懒惰的,所以创建这样的无限列表不会崩溃任何东西(只是不要试图打印它)。

因此,虽然这在理论上可能与我们之前的数学定义相同,但它将结果保存在我们的fibs列表(某种程度上是一种自动记忆)中,而且我们很少遇到在天真的解决方案中可能遇到的问题。为了完整起见,让我们根据新的fibs列表定义fib函数:

fib n = fibs !! n

如果我还没有失去你,那很好,因为那意味着你理解Clojure代码。看:

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

我们列一个清单,fib-seq。它以两个元素[0 1]开始,就像我们的Haskell示例一样。我们用(map + fib-seq (rest fib-seq))对这两个初始元素进行延迟连接-假设resttail在Haskell中所做的相同,我们只是在较低的偏移量处将列表与自身组合,然后将这两个列表与+运算符/函数组合。

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

网友
2楼 · 发布于 2024-05-28 18:10:30

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

a = a + 5

世界贸易基金会?a显然a + 5不同。

如果a = a + 5,那么a + 5 = a

为什么这不管用???

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

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

您认为命令式方法更容易理解,因为您已经习惯了它。

网友
3楼 · 发布于 2024-05-28 18:10:30

我喜欢:

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

它似乎与python/generator版本有一些相似之处。

相关问题 更多 >

    热门问题