Núcleo funcional como canalización en Python

El objetivo principal de esta publicación es mostrar un patrón de arquitectura de poco uso en Python llamado " núcleo funcional - envoltorio imperativo ", en el que la programación funcional se mezcla con la programación imperativa en un intento de negar las deficiencias de cada uno de ellos. Se sabe que los lenguajes funcionales son débiles cuando interactúan con el "mundo real", como la entrada del usuario, la interacción de la GUI u otras E / S.





Este enfoque utiliza la capacidad de Python para trabajar con funciones dentro del paradigma funcional, en el que las funciones pueden manipularse de la misma manera que cualquier otro objeto: pasarse como argumentos a otras funciones, devolverse de funciones e incluirse en secuencias como sus elementos.





Para aquellos que buscan repasar la programación funcional en Python, recomiendo seguir el enlace a mi publicación sobre los conceptos básicos de FP en Python.





Canalización de procesamiento de datos
Canalización de procesamiento de datos

El estilo funcional de la programación está muy cerca de cómo piensa una persona mientras resuelve un problema. “Que se dé x



. Para solucionar el problema con estos datos, es necesario realizar una serie de transformaciones. Primero, aplíqueles f



y obtenga los datos resultantes x'



. Luego aplique a los nuevos datos f2



y obtenga nuevos datos resultantes x''



, etc.





, . , .. . , . , (), .





, , (1) (2) , debug, (3) , .





, . F#:





 2                            
 |> ( fun x -> x + 5)         
 |> ( fun x -> x * x)         
 |> ( fun x -> x.ToString() ) 
      
      



, 2, -. Python, , , , :





#   
 def pipe(data, *fseq):
    for fn in fseq: 
        data = fn(data)
    return data
      
      



Python:





pipe(2,
     lambda x: x + 5,
     lambda x: x * x,
     lambda x: str(x))
      
      



:





add      = lambda x: lambda y: x + y
square   = lambda x: x * x
tostring = lambda x: str(x)

pipe(2,
     add(5),
     square,
     tostring)
      
      



2 , '49'



. reduce



, , pipe .





pipe



: data



fseq



. for



. , data . .. , . pipe . .





. pipe *



. *



.





, , . ,





def my_sum(*args):  #   
    return sum(args)

my_sum(1, 2, 3, 4, 5)
      
      



. ,





def fun(a, b, c, d):
    print(a, b, c, d)

my_list = [1, 2, 3, 4]
fun(*my_list) #    
      
      



- ( ):





class Factory:
    def process(self, input):
        raise NotImplementedError

class Extract(Factory):
    def process(self, input):
        print(" ...")
        output = {}
        return output

class Parse(Factory):
    def process(self, input):
        print(" ...")
        output = {}
        return output

class Load(Factory):
    def process(self, input):
        print(" ...")
        output = {}
        return output

pipe = {  
    ""   : Extract(),
    "" : Parse(),
    "" : Load(),
}

inputs = {}  
#  
for name, instance in pipe.items():  
    inputs = instance.process(inputs)
      
      



:





 ... 
 ... 
 ...
      
      



for



, . - , . « , , , » - , - Erlang.





, , , .





(factorial



) (factorial_rec



). . , . .





, , - ;-), .. .





#    
#    

def main():
    #  ( c   )
    pipe(int(input('   : ')),    
         lambda n: (n, reduce(lambda x, y: x * y, range(1, n + 1))),    
         lambda tup: 
             print(f'  {tup[0]}  {tup[1]}'))        

#   
main()
      
      



:





   : 4 (Enter)
  4  24
      
      



- , .





, .. - - . . .





#    
#     

def get_int(msg=''):
    return int(input(msg))

def main():
    #  1.     
    def factorial_rec(n): 
        fn = lambda n, acc=1: acc if n == 0 else fn(n - 1, acc * n)
        return n, fn(n) 
  
    #  2.  
    def factorial(n):     
        return n, reduce(lambda x, y: x * y, range(1, n + 1)) 
   
    #  
    def indata():
        def validate(n):  #   
            if not isinstance(n, int):
                raise TypeError("   .")
            if not n >= 0:
                raise ValueError("   >= 0.")
            return n        
        msg = '   : '
        return pipe(get_int(msg), validate)
   
    #  
    def outdata():
        def fn(data):
            n, fact = data
            print(f'  {n}  {fact}') 
        return fn

    #  ( )
    pipe(indata(),     # : -       : int
         factorial,    # : int     : 
         outdata())    # :   : -    

#   
main()
      
      



:





   : 4 (Enter)
  4  24
      
      



:





pipe(indata(), 
     factorial, 
     outdata())
      
      



, .. indata



, factorial



outdata



. indata



, . factorial



, , , . outdata



. , indata , .





. -, - . -, .





:





  • . , factorial



    , factorial_rec



    .





pipe(indata(), factorial_rec, outdata())
      
      



  • , .





, – . debug



:





def debug(data):
    print(data) 
    return data
      
      



, :





pipe(indata(), debug, factorial, debug, outdata())
      
      



, :





:





   : 4 (Enter)
4
(4, 24)
      
      



4 24





, factorial



4



, (4, 24)



. , , . , debug



-, .





.





#    
#     

def main():
    # 
    def fibonacci(n, x=0, y=1):
        #  fib  n-  .
        fib = lambda n, x=0, y=1: x if n <= 0 else fib(n - 1, y, x + y)
        #  reduce     acc
        acc = []
        reduce(lambda _, y: acc.append(fib(y)), range(n + 1))
        return n, acc

    #   
    def validate(n):         
        if not isinstance(n, int):
            raise TypeError("   .")
        if not n >= 0:
            raise ValueError("    .")
        if n > 10:
            raise ValueError("     10.")
        return n

    #  
    def indata():
        msg = '      10: '
        return pipe(get_int(msg), validate)

    #  
    def outdata():
        def fn(data):
            n, seq = data
            msg = f' {n}   :'
            print(msg) 
            [print(el) for el in seq]
        return fn

    #  ( )
    pipe(indata(), fibonacci, outdata()) 

#   .
main()

 
      10: 10 (Enter)
 10   :
1
1
2
3
5
8
13
21
34
55
      
      



#    
#    
#   

def main():
    # 
    def range_sum(data):  
        seq, params = data
        fn = lambda start, end: 0 if start > end \
                                  else seq[start] + fn(start + 1, end)
        return fn(*params)

    #  
    def indata():
        seq = [1, 2, 3, 4, 5, 6, 7, 8, 9]    
        params = (2,5)   # params -   start, end
        return seq, params  

    #  
    def outdata():
        def f(data):
            msg = '   2  5   '
            print(msg, format(data), sep='') 
        return f

    #  ( )
    pipe(indata(), range_sum, outdata()) 

#   .
main()

 
   2  5   18
      
      



Python . : . . , , , , , . , .)





Github. Strating Out with Python. , . ,





  • PyCon .





  • , .





  • Youtube « Python - ».





, , Python , map/filter/reduce/zip functools. . , , , .








All Articles