yield 的学习,模拟无限数据 ::-- ZoomQuiet [2007-10-24 04:15:39]

CPUG联盟::

CPUG::门户plone

BPUG

SPUG

ZPUG

SpreadPython Python宣传

Albert Lee <[email protected]>             hide details    12:08 pm (2 minutes ago)  
        reply-to                [email protected]       
        to              "Python.cn@google" <[email protected]>  
        date            Oct 24, 2007 12:08 PM    
        subject         [CPyUG:34110] yield 的学习,模拟无限数据   
        mailed-by               googlegroups.com         

1. FPy 模拟

1.1. yield 的学习,模拟无限数据

对haskell我最喜爱的特性是惰性计算和无限数据结构,以及函数组合。 这些特性在python中是否可以模拟出来呢? 今天作了一些尝试

python中提供了yield 和 decorators, 应当可以模拟出。

1.1.1. 尝试: 无限数据结构

haskell: [1..]

Hugs> take 5 [1..]
[1,2,3,4,5]

Python:

>>> def inf_list1():
i = 0
while 1:
i += 1
yield i

>>> def take(n, it):
cnt = 0
for i in it():
cnt += 1
if cnt > n: break
yield i

>>> take(5, m)

>>> [i for i in take(5, inf_list1)]
[1, 2, 3, 4, 5]

Python 的 take 也实现为一个 generator ,这样的好处是可以串起来执行,比如,我要实现 haskell中的: Hugs> drop 3 $ take 5 [1..] [4,5]

  • 那么再实现一个 drop 函数:

>>> def drop(n, it):
t = 0
for i in it():
t += 1
if t > n:
yield i

>>> def m10():
for i in range(10):
yield i

>>> [i for i in drop(3, m10)]
[3, 4, 5, 6, 7, 8, 9]
>>> [i for i in take(5, m10)]
[0, 1, 2, 3, 4]
>>>
  • 不过,在组合 take 和 drop 的时候遇到了麻烦

>>> drop(3, take(5, m10))

>>> [i for i in drop(3, take(5, m10))]

Traceback (most recent call last):
File "", line 1, in -toplevel-
[i for i in drop(3, take(5, m10))]
File "", line 3, in drop
for i in it():
TypeError: 'generator' object is not callable
>>>
  • 修改下 drop, take 的定义如下:

>>> def take(n, it):
cnt = 0
if callable(it): it=it()
for i in it:
cnt += 1
if cnt > n: break
yield i
>>> def drop(n, it):
cnt = 0
if callable(it): it = it()
for i in it:
cnt += 1
if cnt > n:
yield i

>>> [i for i in take(2, drop(3, take(7, inf_list1)))]
[4, 5]
>>>

1.1.2. 对应 haskell

Hugs> take 2 $ drop 3 $ take 7 [1..]
[4,5]
Hugs>

基本达到目的。

1.1.3. 用itertools模块更简单

Qiangning Hong <[email protected]>             hide details    12:57 pm (8 minutes ago)  
        reply-to                [email protected]       
        to              [email protected]       
        date            Oct 24, 2007 12:57 PM    
        subject         [CPyUG:34115] Re: yield 的学习,模拟无限数据:

!python 
import sys
from itertools import count, islice

def inf_list():
   return count(1)

def take(n, it):
   return islice(it, n)

def drop(n, it):
   return islice(it, n, sys.maxint)

list(take(5, inf_list())  -> [1, 2, 3, 4, 5]
list(drop(3, take(5, inf_list()))) -> [4, 5]
list(take(2, drop(3, take(7, count(1))))) -> [4, 5]

1.2. 函数的组合

比较简单:

Hugs> ((*2) . (+3)) 5
16
  • 在Python

    >>> def m2(x):
           return  x * 2
    
    >>> def add3(x):
           return x + 3
    
    >>> def compose(f, g):
           return lambda x:f(g(x))
    
    >>> compose(m2, add3)
    <function <lambda> at 0x012875F0>
    >>> compose(m2, add3)(5)
    16
    >>>
    

1.2.1. 对compose的装饰:

infix 来自 cookbook

class Infix:
   def __init__(self, function):
       self.function = function
   def __ror__(self, other):
       return Infix(lambda x, self=self, other=other: self.function(other, x))
   def __or__(self, other):
       return self.function(other)
   def __rlshift__(self, other):
       return Infix(lambda x, self=self, other=other: self.function(other, x))
   def __rshift__(self, other):
       return self.function(other)
   def __call__(self, value1, value2):
       return self.function(value1, value2)

# compose
def compose(f, g):
   return lambda x:f(g(x))

o=Infix(compose)

add2 = lambda x:x+2
multi3 = lambda x:x*3
new_f = add2 |o| multi3
print new_f(3)

---

>>> (add2 <<o>> multi3) (3)
11

已经比较接近了

1.2.2. curry

一个函数需要两个参数, 只给出一个参数的话,则返回一个函数,再给一个参数才计算(curry) , 用 decorators 貌似可以实现。 对多参数的处理不知道怎么办了。

组合现在可以这样:

>>> f = fun1 <<o>> fun2 <<o>> fun3
>>> f(1)

看来函数重载真是有很多意想不到的作用,可以实现 DSL了。

   1 from functools import partial
   2 
   3 def allow_partial_args(func):
   4    def _(*args):
   5        rturn partial(func, *args)
   6    return _
   7 
   8 @allow_partial_args
   9 def add(a, b):
  10    return a + b
  11 
  12 add(1)(2) -> 3
  13 add()(1, 2) -> 3
  14 add(1, 2)() -> 3

1.3. 反馈

Name Password4deL ;) :( X-( B-)