admin管理员组

文章数量:1410712

I would like to implement something similar to this OCaml in Python:

let example  = fun v opt_n ->
    let fltr = fun i -> i mod 2 = 0 in
    let fltr = match opt_n with 
        | None -> fltr
        | Some n -> fun i -> (i mod n = 0 && fltr n) in
    fltr v

This is easily composable/extendable, I can add as many predicates as I want at runtime. This is of course a simplified example, in real life I have many optional inclusion/exclusion sets, and predicate checks for membership.

Doing this the naive way in Python fails:

def example(v: int, opt_n=None):
    """
    doesn't work!
    """
    # doesn't need to be a lambda, an explicitely defined function fails too
    fltr = lambda i: i % 2 == 0
    if opt_n is not None:
        # fails miserably -> maximum recursion depth exceeded
        fltr = lambda i: fltr(i) and i % opt_n == 0
    return fltr(v)

example(10, 5) 

This is annoying because it seems that since fltr can only appear once on the left side of the assignment, I have to inline the initial fltr in every case afterward:

def example(v: int, opt_n=None, opt_m=None):
    """annoying but works"""
    fltr = None
    # some inital filters
    pred_0 = lambda _: True # do some real checks ...
    pred_1 = lambda _: True  # do some real checks ...
    if opt_n is not None:
        # fltr is inlined, only appears on left side,  now it works
        fltr = lambda i: pred_0(i) and pred_1(i) and opt_n % 2 == 0
    if opt_m is not None:
        # much repetition
        fltr = lambda i: pred_0(i) and pred_1(i) and opt_n % 3 == 0
    if fltr is None:
        # inlined again
        fltr = lambda i: pred_0(i) and pred_1(i)
    return fltr(v)

Is there any way to fix my mess, maybe I am missing something, and/or what is the recommended way to compose predicates in Python?

I would like to implement something similar to this OCaml in Python:

let example  = fun v opt_n ->
    let fltr = fun i -> i mod 2 = 0 in
    let fltr = match opt_n with 
        | None -> fltr
        | Some n -> fun i -> (i mod n = 0 && fltr n) in
    fltr v

This is easily composable/extendable, I can add as many predicates as I want at runtime. This is of course a simplified example, in real life I have many optional inclusion/exclusion sets, and predicate checks for membership.

Doing this the naive way in Python fails:

def example(v: int, opt_n=None):
    """
    doesn't work!
    """
    # doesn't need to be a lambda, an explicitely defined function fails too
    fltr = lambda i: i % 2 == 0
    if opt_n is not None:
        # fails miserably -> maximum recursion depth exceeded
        fltr = lambda i: fltr(i) and i % opt_n == 0
    return fltr(v)

example(10, 5) 

This is annoying because it seems that since fltr can only appear once on the left side of the assignment, I have to inline the initial fltr in every case afterward:

def example(v: int, opt_n=None, opt_m=None):
    """annoying but works"""
    fltr = None
    # some inital filters
    pred_0 = lambda _: True # do some real checks ...
    pred_1 = lambda _: True  # do some real checks ...
    if opt_n is not None:
        # fltr is inlined, only appears on left side,  now it works
        fltr = lambda i: pred_0(i) and pred_1(i) and opt_n % 2 == 0
    if opt_m is not None:
        # much repetition
        fltr = lambda i: pred_0(i) and pred_1(i) and opt_n % 3 == 0
    if fltr is None:
        # inlined again
        fltr = lambda i: pred_0(i) and pred_1(i)
    return fltr(v)

Is there any way to fix my mess, maybe I am missing something, and/or what is the recommended way to compose predicates in Python?

Share Improve this question edited Mar 20 at 0:19 Chris 36.9k6 gold badges33 silver badges55 bronze badges asked Mar 9 at 14:55 DenisDenis 1,5952 gold badges18 silver badges31 bronze badges 5
  • maybe write it as normal function instead of lambda – furas Commented Mar 9 at 15:53
  • Can you add an explanation in plain English what you intend the example function to do and how/where you intend to be able to add predicates? What does opt_n mean? – mkrieger1 Commented Mar 9 at 15:54
  • It's interesting that the OCaml you show as an example is not valid OCaml code. – Chris Commented Mar 19 at 18:53
  • 1 @Chris shameful. Edited. – Denis Commented Mar 19 at 22:02
  • It's not the most popular programming language out there, so it's not crazy no one else caught that. – Chris Commented Mar 20 at 0:17
Add a comment  | 

1 Answer 1

Reset to default 3

When you write

fltr = lambda i: fltr(i) and i % opt_n == 0

fltr remains a free variable inside the lambda expression, and will be looked up when the function is called; it's not bound to the old definition of fltr in place when you evaluate the lambda expression.

You need some way to do early binding; one option is to bind the current value of fltr to a new variable that's local to the lambda expression, namely another parameter:

fltr = lambda i, cf=fltr: cf(i) and i % opt_n == 0

Not the cleanest solution. If you don't mind an additional function call, you can define an explicit composition function:

def pred_and(f, g):
    return lambda x: f(x) and g(x)

then use that to compose the old fltr with another predicate to produce a new filter.

def example(v: int, opt_n=None):
    fltr = lambda i: i % 2 == 0
    if opt_n is not None:
        # fails miserably -> maximum recursion depth exceeded
        fltr = pred_and(fltr, lambda i: i % opt_n == 0)
    return fltr(v))

(I don't know OCaml very well, but pred_and is somewhat like the use of an applicative functor in Haskell, e.g. pred_and = liftA2 (&&).)

本文标签: functional programmingPython39s predicate compositionStack Overflow