Python 延迟执行

4 投票
4 回答
676 浏览
提问于 2025-04-16 20:24

在Lisp中,要实现延迟列表的流,推荐使用Lisp的宏。

(defmacro cons-stream (a b)
   (cons ,a (delay ,b)))

(defmacro delay (expr)
  `(memo-proc (lambda () ,expr)))

那么在Python和Perl中,怎么做同样的事情呢?

补充一下,像Python和Perl这样的语言,能不能使用这么酷的结构,比如流呢?

(define primes (sieve (integers-starting-from 2)))

4 个回答

3

在Perl语言中,你可以使用匿名子程序,这个概念有点像LISP语言里的lambda表达式。想了解更多的例子,可以去《Higher Order Perl》的第六章看看。

5

在Python中,最接近的结构可能就是生成器表达式。

在Perl中,没有原生的懒加载列表,但这个语言提供了构建懒加载列表所需的所有基本功能。我写了一个叫做 List::Gen 的懒加载列表库,可以在CPAN上找到。

use List::Gen '*';

my $primes = <2..>->filter(\&is_prime);  # where you provide &is_prime

say "@$primes[0..10]";  # lazily finds the first 11 primes

这个 <2..> 的部分可以更详细地写成 range(2, 9**9**9)

4

Perl

runrig 提出了来自 Mark Dominus 的优秀书籍 Higher Order Perl 中的一些技巧。使用 HOP 提供的免费示例代码中的 Stream 模块,我们可以实现埃拉托斯特尼筛法。

#! /usr/bin/env perl

use strict;
use warnings;

use Stream qw/ filter head node promise show tail upfrom /;

use subs 'sieve';  # no parens on recursive calls
sub sieve {
  my($s) = @_;
  my $n = head $s;
  node $n, promise { sieve filter { $_[0] % $n != 0 } tail $s };
}

sub primes { sieve upfrom 2 }

show primes, 10;

输出结果:

$ ./primes
2 3 5 7 11 13 17 19 23 29

Python

借用 alexbowe 的一段代码,在 Python 中使用流来实现筛法的代码是:

#! /usr/bin/env python

null_stream = (None, None)

def reduce(f, result, stream):
    if stream is null_stream: return result
    return reduce(f, f(result, head(stream)), tail(stream))

def take(N, stream):
    if N <= 0 or stream is null_stream: return null_stream
    return (head(stream), lambda: take(N-1, tail(stream)))

def filter(pred, stream):
    if stream is null_stream: return null_stream
    if pred(head(stream)):
        return (head(stream), lambda: filter(pred, tail(stream)))
    return filter(pred, tail(stream))

def integers_from(N): return (N, lambda: integers_from(N+1))
def head((H, _)): return H
def tail((_, T)): return T()
def to_array(stream): return reduce(lambda a, x: a + [x], [], stream)

def sieve(stream):
    if stream is null_stream: return null_stream
    h = head(stream)
    return (h, lambda: sieve(filter(lambda x: x%h != 0, tail(stream))))

def primes(): return sieve(integers_from(2))

print to_array(take(10, primes()))

输出结果:

$ ./prymes 
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

其他可能性

在某些编程语言中,流的模式是看不见的。例如,Haskell 语言有懒惰求值的特性,所以你可以这样定义 primes

primes = sieve [2 ..]
  where sieve (x:xs) =
          let remains = filter (not . isMultipleOf x) xs
          in x : sieve remains
        isMultipleOf a b = b `mod` a == 0

撰写回答