Using the fn.py library for functional programming in Python _python

Source: Internet
Author: User
Tags assert documentation error handling modifier

Although Python is in fact not a purely functional programming language, it is itself a multilingual language and gives you enough freedom to use functional programming. Functional style has a variety of theoretical and practical benefits (you can find this list in Python's documentation):

    • form can be verified
    • Modular Nature
    • Combination of
    • Easy to debug and test

Although this list has been described clearly enough, I still like Michael O.church's description of the benefits of functional programming in his article, "Very few functional programs decay (functional programs rarely rot)". I talked about the use of functional methods in Python in "functional programming with Python" during the Pycon UA No. 2012 session. I also mentioned that when you try to write readable and maintainable functional code in Python, you'll quickly find a lot of problems.

The Fn.py class Library was born to deal with these problems. Although it is not possible to solve all the problems, it is a "battery" for developers who want to get the most value from functional programming, even if it is in the command-style dominant program. So, what does it all have?
Scala-style lambda definition

The syntax for creating lambda functions in Python is very lengthy, to compare:

Python

Map (Lambda x:x*2, [1,2,3])

Scala

Copy Code code as follows:

List (1,2,3). Map (_*2)

Clojure

Copy Code code as follows:

(Map # (*% 2) ' (1 2 3))

Haskell

Copy Code code as follows:

Map (2*) [1,2,3]

Inspired by Scala, fn.py provides a special _ object to simplify the lambda syntax.

From FN Import _

Assert (_ + _) (5) =
assert List (Map (_ * 2, Range (5)) = = [0,2,4,6,8]
assert list (filte R (_ < 10, [9,10,11]) = = [9]

In addition, there are many scenarios where you can use _: all arithmetic operations, attribute parsing, method invocation, and slicing algorithms. If you're not sure what your function will do, you can print the results:

From FN Import _ 

Print (_ + 2) # (x1) => (x1 + 2) " 
Print (_ + _ * _) #" (X1, x2, x3) => (x1 + (x2 * x3)) "

Flow (stream) and the declaration of an infinite sequence

A Scala-style lazy evaluation (lazy-evaluated) stream. The basic idea is to take the value on demand for each new element and share the calculated element values in all the iterations that are created. The Stream object supports the << operator, which represents the push of new elements as needed.

The processing of an infinite sequence by an inert value stream is a powerful abstraction. Let's take a look at how to compute a Fibonacci sequence in a functional programming language.

Haskell

Copy Code code as follows:
fibs = 0:1: Zipwith (+) fibs (tail fibs)

Clojure

Copy Code code as follows:
(Def fib (lazy-cat [0 1] (map + fib (rest fib)))

Scala

Copy Code code as follows:
def fibs:stream[int] =
0 #:: 1 #:: Fibs.zip (Fibs.tail). Map{case (a,b) => A + b}

Now you can use the same way in Python:

From the FN import Stream 
from the Fn.iters import take, drop, map from
operator import add

f = Stream ()
fib = f & lt;< [0, 1] << map (add, F, drop (1, f))

assert list (take, fib) = = [0,1,1,2,3,5,8,13,21,34]
assert fib[ = = = 6765
assert list (fib[30:35]) = = [832040,1346269,2178309,3524578,5702887]

Trampoline (trampolines) modifier

Fn.recur.tco is a temporary solution that does not require large stack space allocations to handle TCO. Let's start with a recursive factorial calculation example:

def fact (n):
   If n = 0:return 1 return
   N * Fact (N-1)

This can work, but the implementation is very bad. Why, then? It consumes a lot of storage space because it recursively saves the previous computed value to work out the final result. If you perform this function on a large n value (exceeding the value of Sys.getrecursionlimit ()), CPython will fail 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 a serious error in your code.

How do we optimize this program? The answer is simple, just 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 way better? Because you do not need to keep the previous value to calculate the final result. You can view more of the content of the tail recursive call optimization on Wikipedia. But...... The Python interpreter executes this function in the same way as the previous function, and the result is that you don't get any optimizations.

Fn.recur.tco provides you with a mechanism that allows you to use a "trampoline" approach to achieve a certain tail-recursive optimization. The same approach is also used in languages such as Clojure, where the main idea is to convert a 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, AC C*n)

@recur. TCO is a modifier that turns your function execution into a while loop and verifies its output:

    • (False, result) on behalf of the finished run
    • (True, args, Kwargs) means we want to continue calling the function and passing different arguments
    • (func, args, Kwargs) to toggle the function to be executed in the while loop

Error handling in functional style

If you have a request class, you can get the corresponding value according to the parameter name passed in. To have the return value formatted as all uppercase, Non-null, and to remove the string of trailing spaces, you need to write this:

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 () C10/>if len (param) = = 0:
     fixed = ""
   else:
    fixed = Param.upper () 

Well, it looks a little odd. Use Fn.monad.Option to modify your code, which represents an optional value, and each option instance can represent a full or empty (this is also inspired by the option in Scala). It provides a convenient way for you to write long sequences of operations, and removes many If/else statement blocks.

From operator import Methodcaller from
fn.monad import optionable

class 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 calls to try to complete the calculation. For example, you have a request class that has several optional attributes such as Type,mimetype and URLs, and you need to use at least one attribute value to parse 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 
     Or_call (from_mimetype, request) \ # or. Check "MimeType" key< C18/>.or_call (from_extension, request) \ # or ... get "url" and check extension 
     . get_or ("application/undefined")

The rest of the matter?

I only describe a small part of the class library, and you can also find and use the following features:

    • 22 additional Itertools code snippets to extend the functionality of the built-in module
    • Unifying the use of Python 2 and Python 3 iterators (iterator) (such as Range,map and Filtter, and so on) is useful when using a cross-version class library
    • It provides a simple syntax for functional combination and partial function application.
    • Additional operators are provided to use higher-order functions (apply,flip, etc.)

Work in progress

Since the release of this class library on GitHub, I have received many revisers views, comments and suggestions from the community, as well as patches and fixes. I'm also continuing to enhance existing functionality and provide new features. The recent road map includes the following elements:

    • Add more operators for use with iterable objects (such as FOLDL,FOLDR)
    • More monad, such as Fn.monad.Either, to handle error logging
    • Provides c-accelerator for most module
    • To simplify Lambda Arg1:lambda arg2: ... The generator of the curry function provided in the form
    • More documentation, more tests, 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.