Using the Fn. py library for functional programming in Python, fn. pypython

Source: Internet
Author: User

Using the Fn. py library for functional programming in Python, fn. pypython

Although Python is not actually a pure functional programming language, it is a multi-model language and gives you sufficient freedom to use functional programming. The functional style has various theoretical and practical advantages (you can find this list in the Python documentation ):

  • Form verifiable
  • Adequacy
  • Combination
  • Easy to debug and Test

Although this list is clearly described, I still like Michael O. in his article "function programs rarely break down (Functional programs rarely rot)", Church describes the advantages of function programming. In my lecture "Functional Programming with Python" during PyCon UA 2012, I talked about using Functional methods in Python. I also mentioned that when you try to write readable and maintainable functional code in Python, you will soon find many problems.

The fn. py class library was born to address these problems. Although it cannot solve all the problems, it is a "battery" for developers who want to obtain the greatest value from functional programming ", even in programs where imperative methods are dominant, they can also play a role. So what are there in it?
Scala-style Lambda Definition

The syntax for creating a Lambda function in Python is very lengthy. Let's take a look at the following:

Python

map(lambda x: x*2, [1,2,3])

Scala

Copy codeThe Code is as follows:
List (1, 2, 3). map (_ * 2)

Clojure

Copy codeThe Code is as follows:
(Map # (* % 2) '(1 2 3 ))

Haskell

Copy codeThe Code is as follows:
Map (2 *) [1, 2, 3]

Inspired by Scala, Fn. py provides a special _ OBJECT To simplify Lambda syntax.

from fn import _assert (_ + _)(10, 5) = 15assert list(map(_ * 2, range(5))) == [0,2,4,6,8]assert list(filter(_ < 10, [9,10,11])) == [9]

In addition, there are many scenarios to use _: All arithmetic operations, attribute parsing, method calls, And sharding algorithms. If you are not sure what your function will do, you can print the result:

from fn import _ print (_ + 2) # "(x1) => (x1 + 2)" print (_ + _ * _) # "(x1, x2, x3) => (x1 + (x2 * x3))"

Stream and infinite sequence Declaration

Scala-style Lazy-evaluated stream. The basic idea is to take values for each new element as needed and share the calculated element values in all the iterations created. The Stream object supports the <operator, which means to push new elements into it as needed.

Processing of infinite sequences by the inert evaluate stream is a powerful abstraction. Let's look at how a Fibonacci sequence is computed in functional programming languages.

Haskell

Copy codeThe Code is as follows: FIG = 0: 1: zipWith (+) fibs (tail fibs)

Clojure

Copy codeThe Code is as follows: (def fib (lazy-cat [0 1] (map + fib (rest fib ))))

Scala

Copy codeThe Code is as follows: def fibs: Stream [Int] =
0 #: 1 #: maid. map {case (a, B) => a + B}

Now you can use the same method in Python:

from fn import Stream from fn.iters import take, drop, mapfrom operator import addf = Stream()fib = f << [0, 1] << map(add, f, drop(1, f))assert list(take(10, fib)) == [0,1,1,2,3,5,8,13,21,34]assert fib[20] == 6765assert list(fib[30:35]) == [832040,1346269,2178309,3524578,5702887]

Trampoline Modifier

Fn. recur. tco is a temporary solution that can process TCO without having to allocate a large amount of stack space. Let's start with a recursive factorial calculation example:

def fact(n):   if n == 0: return 1   return n * fact(n-1)

This method also works, but the implementation is very bad. Why? Because it recursively saves the previous calculation value to calculate the final result, it consumes a lot of storage space. If you execute this function on a very large n value (beyond the value of sys. getrecursionlimit (), CPython will stop failing in this way:

>>> import sys>>> fact(sys.getrecursionlimit() * 2)... many many lines of stacktrace ...RuntimeError: maximum recursion depth exceeded

This is also a good thing, at least it avoids serious errors in your code.

How can we optimize this solution? The answer is simple. You only need to change the function to use tail recursion:

def fact(n, acc=1):   if n == 0: return acc   return fact(n-1, acc*n)

Why is this method better? Because you do not need to keep the previous value to calculate the final result. You can view more Optimization content for tail recursive calls on Wikipedia. But ...... The Python interpreter executes this function in the same way as the previous function. The result is that you have not been optimized.

Fn. recur. tco provides you with a mechanism that allows you to use the "trampoline" method to get a certain degree of tail recursive optimization. The same method is also used in languages such as Clojure. The main idea is to convert the function call sequence into a while loop.

from fn import recur@recur.tco def fact(n, acc=1):   if n == 0: return False, acc   return True, (n-1, acc*n)

@ Recur. tco is a modifier that converts your function execution into a while loop and verifies its output content:

  • (False, result) indicates that the operation is completed.
  • (True, args, kwargs) indicates that we want to continue calling the function and passing different parameters.
  • (Func, args, kwargs) indicates switching the function to be executed in the while loop.

Function-style error handling

Assume that you have a Request class, and you can obtain the corresponding value based on the input parameter name. To convert the return value to a string in the format of uppercase, non-null, and trailing spaces, you need to write the following:

class Request(dict):   def parameter(self, name):     return self.get(name, None)r = Request(testing="Fixed", empty=" ")param = r.parameter("testing")if param is None:   fixed = ""else:      param = param.strip()   if len(param) == 0:     fixed = ""   else:    fixed = param.upper() 

Yes, it looks odd. Use fn. monad. Option to modify your code. It represents an optional value. Each Option instance can represent a Full or Empty (this is also inspired by the Option in Scala ). It provides a simple method for writing long run computing sequences, and removes many if/else statement blocks.

from operator import methodcallerfrom fn.monad import optionableclass Request(dict):   @optionable   def parameter(self, name):     return self.get(name, None)r = Request(testing="Fixed", empty=" ")fixed = r.parameter("testing")      .map(methodcaller("strip"))      .filter(len)      .map(methodcaller("upper"))      .get_or("")

Fn. monad. Option. or_call is a convenient method that allows you to make multiple call attempts to complete computation. For example, you have a Request class that has several optional attributes such as type, mimetype, and url. You need to use at least one attribute value to analyze its "request type ":

from fn.monad import Option request = dict(url="face.png", mimetype="PNG") tp = Option \      .from_value(request.get("type", None)) \ # check "type" key first      .or_call(from_mimetype, request) \ # or.. check "mimetype" key      .or_call(from_extension, request) \ # or... get "url" and check extension      .get_or("application/undefined")

Other issues?

I only described a small part of the class library. You can also find and use the following features:

  • 22 additional itertools code segments to extend the additional functions of the built-in module
  • Unify the use of iterator (such as range, map, and filtter) of Python 2 and Python 3, which is useful for cross-version class libraries.
  • It provides simple syntax for function combinations and partial function applications.
  • Additional operators are provided for the use of higher-order functions (apply, flip, and so on ).

Ongoing work

Since the release of this class library on Github, I have received many views, opinions and suggestions from the community, as well as patches and fixes. I am also continuing to enhance my existing features and provide new features. The recent Roadmap includes the following:

  • Add more operators to use iterable objects, such as foldl and foldr.
  • More monad, such as fn. monad. Either, to handle error records
  • Provides C-accelerator for most modules
  • To simplify lambda arg1: lambda arg2 :... Form provided by the curry Function Generator
  • More documents, more tests, and more sample code

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.