代码之家  ›  专栏  ›  技术社区  ›  Vijay Mathew Chor-ming Lung

各种语言的python生成器[关闭]

  •  19
  • Vijay Mathew Chor-ming Lung  · 技术社区  · 15 年前

    如何用您最喜欢的语言模拟Python风格的生成器?我发现 this 一个在计划中。看到其他实现一定很有意思,特别是在那些没有一流延续的语言中。

    9 回复  |  直到 12 年前
        1
  •  8
  •   Nick Dandoulakis    15 年前

    下面是C++中使用光纤模拟生成器的例子:

    Yield Return Iterator for Native C++ Using Fibers

    “yield return”迭代器是一个 为创建的语言功能 原因之一:简单。它是 通常更容易迭代 在整个集合中,存储所有 局部变量所需的上下文, 与其制造复杂的东西, 存储其 跨后续检索的状态 操作。

    还有 原始的 C例程 setjmp, longjmp 达到类似的效果。
    (Lua coroutines 采用上述方法实施)

        2
  •  11
  •   Rainer Joswig mmmmmm    14 年前

    在Lisp/Scheme中,我根本不会使用收益率。

    “yield”需要某种语言中的共同程序或延续功能。许多对yield的使用都可以用一种更简单的功能方式实现。

    收益率基本上与著名的来自经营者有关。;-)在这里,根据执行上下文的不同,某个地方的调用可能导致其他某个例程中的不同位置。所以一个例程突然有多个入口点,它们的顺序是在运行时确定的。对于简单的使用,这可能是好的,但我认为对于更复杂的代码,关于代码的推理将变得更加困难。

    以问题中链接的方案示例为例:

    (define/y (step) 
      (yield 1)
      (yield 2)
      (yield 3)
      'finished)
    
    (list (step) (step) (step))
    

    多次调用(step)会返回不同的值。

    我将创建一个闭包:

    (define step
      (let ((state '(1 2 3 finished)))
        (lambda ()
          (pop state))))
    

    这将上述函数分解为两个不同的东西:一个携带状态的变量和一个改变状态的简单函数。状态不再隐式编码到执行序列中。

     (list (step) (step) (step))))
    

    对于产量的其他用途,我们可以想象类似的解决方案。

    把它和 generators from the Common Lisp SERIES library :

    (let ((x (generator (scan '(1 2 3 finished)))))
      (list (next-in x)
            (next-in x)
            (next-in x)))
    

    如果我们从另一个答案来看这个python示例

    def simpleRange(n):
        for i in xrange(n):
            yield i
    
    for n in simpleRange(5):
         print(n)
    

    我们可以看到它复制了控制结构。调用位置和生成器都使用A作为迭代控制结构。使用闭包,我们只需提供状态转换代码,就可以避免在生成器内部使用控制结构。

        3
  •  3
  •   Eli Grey    15 年前

    在javascript 1.7+中,我通常只需要添加几个括号和括号。其他一切都差不多。Javascript1.7引入了pythonic生成器和迭代器。

    生成器表达式:

    # Python
    (x + 1 for x in y if x > 100)
    
    // JavaScript 1.8+
    (x + 1 for (x in y) if (x > 100))
    

    发电机

    # Python
    def simpleRange(n):
        for i in xrange(n):
            yield i
    
    for n in simpleRange(5):
         print(n)
    
    
    // JavaScript 1.7+
    function simpleRange(n) {
        for (let i = 0; i < n; i++)
            yield i;
    }
    
    for (n in simpleRange(5))
        print(n);
    

    列表/数组理解

    # Python
    [x + 1 for x in y if x > 100]
    
    // JavaScript 1.7+
    [x + 1 for (x in y) if (x > 100)]
    
        4
  •  3
  •   c-smile    12 年前

    使用C++ Generators

    简单距离发生器声明:

    $generator(range)
    {
       int i;
       int _max;
       int _min;
    
       range(int minv, int maxv):_max(maxv),_min(minv) {}
    
       $emit(int) // will emit int values. Start of body of the generator.
          for (i = _min; i <= _max; ++i)
             $yield(i); 
       $stop;
    };
    

    用途:

    range r10(1,10);
    for(int n; r10(n);) 
       printf("%d\n",n);
    

    意志输出

    1
    2 
    ...
    10
    
        5
  •  3
  •   Vsevolod Dyomkin    12 年前

    对于@dmitry_vk关于常见Lisp的回答,我将在Lisp中添加一点,实际上,并不真正需要生成器。它们的用例被闭包、特殊变量和宏的不同应用程序完全覆盖,而不需要学习新结构的额外概念开销。

    有时甚至内置结构也能工作。让我们看一下python wiki中的示例:

    # add squares less than 100
    from itertools import count, takewhile
    
    sum = 0
    square = (i*i for i in count())
    bounded_squares = takewhile(lambda x: x < 100, square)
    for i in bounded_squares:
       sum += i
    

    使用 loop 它可以以更直接的方式实现:

    CL-USER> (loop :for i :from 0 :while (< i 100) :sum (expt i 2))
    328350
    

    AS 比巨蟒的多功能 for 这里不需要引入特殊的语法。

    让我们考虑另一个用例-在某个自定义树上迭代。假设树由 node 指向他们的 children .

    (defstruct node
      data
      children)
    

    我们可以用一个非常小和简单的宏遍历任何树/子树。

    (defmacro dotree ((var root &optional result-form) &body body)
      `(block nil
         (labels ((traverse (node)
                    (let ((,var node))
                      ,@body
                      (dolist (child (children node))
                        (traverse child))
                      ,result-form)))
           (when-it ,root
             (traverse it)))))
    

    (警告:为了更清楚起见,我没有使用 gensym 但你应该这样做)。

    下面是它的用法示例-获取所有叶节点的列表:

    (let (rez)
      (dotree (node tree (reverse rez))
        (when (leafp node)
          (push node rez))))
    

    这看起来和标准一样 dolist 宏。和一样 道利斯特 您可以随时通过调用 return .

    总的来说,我还没有看到 实际的 在Lisp中无法以较不复杂的方式实现的生成器使用示例。

    另外,你也可以看看 SERIES Lisp库,实现了与90年代的生成器类似的概念。 CLAZY -从2000年底开始。

        6
  •  1
  •   Dario    15 年前

    Monads 可以用来表示生成器(即使语义略有不同)。

    所以任何允许我们在特殊语法中定义一元操作的语言都可以在这里使用。

    • vb.net/c(linq-但是c已经得到 yield return )
    • scala(供理解)
    • haskell(do符号)
    • f/ocaml(计算表达式/执行)

    Ruby可以通过其内置的延续功能来模拟生成器。

        7
  •  1
  •   dmitry_vk    15 年前

    虽然普通的lisp没有本机的延续,但是允许使用cps转换器创建分隔的延续,比如 cl-cont . 因此,公共lisp中的生成器可以用与方案生成器几乎相同的方式编写。

    顺便说一下,基于延续的生成器有一个特性是Python和C生成器所缺少的: yield 可以在生成器函数调用的动态范围内调用。python和c生成器允许 产量 只能放在发电机内部。

        8
  •  1
  •   horseyguy    14 年前

    红宝石:

    发电机功能:

    def simple_range(n)
        Enumerator.new do |y|    
            (0..n).each { |v| y.yield(v) }
        end
    end
    
        9
  •  1
  •   c-smile    12 年前

    一般来说 yield 在具有第一类函数的语言中是多余的。 例如在 TIScript 你可以这样做发电机:

    发电机。注意,它返回内部函数。

    function range( from, to )
    {
      var idx = from - 1;
      return function() { if( ++idx <= to ) return idx; } // yields value on call
    }
    

    其用途:

    for( var item in range(12,24) )
      stdout << item << " ";
    

    for(elem in source) 在Tiscript中与JS略有不同。如果source是一个函数,它将被调用,并将其返回值赋给 elem 直到函数不返回void(空函数的默认返回值)。