Use Python yield for stream computing mode

Source: Internet
Author: User

The method used in the previous article "How to guess Y combinator" is too complicated. In fact, the idea of implementing recursion in Lambda calculation is very simple, that is, the function passes itself into the function as the first parameter, and then the simple Lambda transformation extracts Y combinator. Well, the following is the text of this article:

Bytes ------------------------------------------------------------------------------------

Yesterday fengidri showed me how to use yield, which inspired me very much-you can use yield to implement the stream computing mode described in SiC.

The so-called stream, that is, stream, is essentially a list of inert values. Python Generator is also a stream.

The following is a simple integer sequence stream generation function:

def integers_starting_from(i):    while True:        yield i        i += 1

This function generates a stream, which is an infinite integer list starting with I (of course, If I is passed in a non-integer, the output sequence is not an integer at this time ). Each time you call next (), an integer is returned:

Stream = integers_starting_from (3) print stream. next () # print 3 print stream. next () # print 4

We know that there are two very common functions for sequence Transformation: map and filter. The following code implements stream_map and stream_filter for the stream:

def stream_map(func, stream):    while True:        yield func(stream.next())def stream_filter(pred, stream):    while True:        x = stream.next()        if pred(x):            yield x

Stream_map transforms each element of stream based on the function func. stream_filter filters out the elements in stream that do not meet the conditions described by the function pred. These two functions are often used in subsequent examples. Next let's take a look at what we can do with stream.

Returns the prime number sequence using the 'distinct' method.

The 'distinct' method calculates the prime number sequence as follows:

1 and 2 are prime numbers. Remove (filter) the numbers that are multiples of 2 from the integers after 2 );

2. 3 is a prime number, and the number in the integer after 3 is a multiple of 3 is deleted;

Step 3 and Step 4 have been deleted. 5 is a prime number. Delete the number that is multiples of 5 in the integer after 5;

......

Repeat this process, and the last thing left behind is the prime number sequence. Based on the stream computing model, every time a prime number p is extracted, the stream following the prime number p is filtered out without the entire prime number p. The following code is used:

Def sieve (): def divisible (x): return lambda e: e % x! = 0 stream = integers_starting_from (2) while True: x = stream. next () yield x stream = stream_filter (divisible (x), # lambda e: e % x! = 0, <-- lambda cannot be used here, otherwise the value of x will not cause a filtering error. Stream)

The first stream is an integer Sequence starting with 2. Each time a number x is taken from the stream (2 is obtained for the first time, followed by a subsequent prime number), a new filter condition divisible (x) is created, and stream_filter is used to filter the sequence. For example:

1. 2 is returned for the first call. stream becomes: {x | x <-[3, 4,...], x % 2! = 0}

2. For the second call, 3 is returned, and stream becomes: {x | x <-[4, 5,...], x % 2! = 0, x % 3! = 0}

3. For the third call, 5 is returned (because 4 is filtered out), and stream becomes: {x | x <-[6, 7,...], x % 2! = 0, x % 3! = 0, x % 5! = 0}

......

Only the prime number is left. Run the following code to print the first 100 prime numbers:

def printn(n, stream):    for _ in xrange(n):        print stream.next(),    printprintn(100, sieve())

Output: 2 3 5 7 11 13 17 19 23 29 31 37 41 47 53 59 61 67 71 73 79 83 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541

Use Level and calculation \ (\ pi \)

\ (\ Pi \) can be calculated using this series:

$ {\ Pi \ over 4} = 1-{1 \ over 2} + {1 \ over 3}-{1 \ over 4} + \ ldots $

First, write a stream that generates each entry of this level:

def pi_summands():    n = 1    sign = 1    while True:        yield 1.0 / n * sign        n += 2        sign *= -1

Next, define a stream transformation. This transformation inputs a stream and outputs the parts and sequences of the stream. That is to say, the first item of the output stream is the first item of stream, the second item is the first item of stream and the second item, and the third item is the first item of stream plus the second item and the third item ......

def partial_sums(stream):    acc = stream.next()    yield acc    while True:        acc += stream.next()        yield acc

As a result, the partial part of the above series and the four times of the sequence are a series approaching \ (\ pi:

Def pi_stream (): return stream_map (lambda x: 4 * x, partial_sums (pi_summands () printn (10, pi_stream () # print the first 10 approximate values

Output: 4.0 2.66666666667 3.46666666667 2.89523809524 3.33968253968 2.97604617605 3.28373848374 3.01707181707 3.25236593472 3.04183961893

Pi_stream's slow convergence speed is terrible, but there is a technique called sequence accelerator that can speed up pi_stream's convergence speed. This accelerator was discovered by Euler. Using the following transformation for the convergence sequence will greatly accelerate the convergence of the original sequence:

$ S _ {n + 1}-{(S _ {n + 1}-S_n) ^ 2 \ over S _ {n-1}-2S_n + S _ {n_1 }}$ $

\ (S_n \) is the \ (n \) of the original sequence. In our problem, it is the \ (n \) item of pi_stream. Code for Accelerator conversion:

def euler_transform(stream):    def transform(s0, s1, s2):        ds = s2 - s1        return s2 - ds * ds / (s0 + s2 - 2 * s1)    s0 = stream.next()    s1 = stream.next()    while True:        s2 = stream.next()        yield transform(s0, s1, s2)        s0, s1 = s1, s2

Use the accelerator to help compute \ (\ pi \):

printn(10, euler_transform(pi_stream()))

Output: 3.16666666667 3.13333333333 3.14523809524 3.13968253968 3.14271284271 3.14088134088 3.14207181707 3.14125482361 3.14183961893 3.1414067185

The convergence speed is much faster, and the tenth item is closer to \ (\ pi \) than the original one.

Sequence acceleration can be performed multiple times:

# Accelerate 2 printn (10, euler_transform (pi_stream () # accelerate 3 printn (10, euler_transform (pi_stream ()))))

Output of two acceleration times: 3.14210526316 3.14145021645 3.141643324 3.1415712902 3.1416028416 3.14158732095 3.14159565524 3.14159086271 3.14159377424 3.14159192394

Output of three acceleration times: 3.14159935732 3.1415908604 3.14159323124 3.14159243844 3.14159274346 3.14159261243 3.14159267391 3.14159264291 3.14159265951 3.14159265016

In addition, SICP also describes a "Super accelerator" technique. The principle is to take the diagonal line, and the item that gets closer to the back is accelerated more than once. I will not elaborate on it here. paste the code and output directly.

Def accelerated_sequence (transform, stream): s = stream while True: s, s_bak = tee (s) yield s. next () s = transform (s_bak) # print the first nine printn (9, accelerated_sequence (euler_transform, pi_stream ()))

Output: 4.0 3.16666666667 3.14210526316 3.14159935732 3.14159271403 3.14159265398 3.14159265359 3.14159265359 3.14159265359

It can be seen that there has been no change since the first item. The first item of this sequence has been accurate to 14 digits, and the original sequence needs to be calculated to \ (10 ^ {13 }\) to reach this precision. In addition, we cannot calculate 10th items here-when we calculate 10th items, the program will be as scattered as the kaifang machine of Li weigong-floating point overflow.

Analog Circuit for Calculation

Let's look at the problem of seeking parts and parts. The sum is equivalent to a circuit with feedback:

The Code focuses on the partial_sums_with_init function. Lambda is used here to make a technique for delaying the evaluation, because feedback (that is, the feedback line) is the final output, and the final output requires the feedback input (the first item is not used, the first item returns the initial value init ). This part is not detailed, and the code should be clearly written. Instead, it is superfluous to draw a picture in words. This analog circuit can be used for integral, differential numerical calculation, and numerical calculation of differential equations.

---------------------------------- I am the last split line ---------------------------------------------------------

Finally, we should pay special attention to the "Side effects" of the stream computing mode implemented by yield ". The stream (Generator) changes the status every time it calls next (). Therefore, after a stream is put into a transform, it cannot be put into another transform.

The complete code can be downloaded at https://github.com/skabyy/palestra/blob/master/python/stream/stream.py.

By the way, SICP is short for "The construction and interpretation of computers". Recently, I have just read a very interesting book. I suggest you read it.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.