• Re: Top level of a recursive function

    From Stefan Ram@21:1/5 to Michael F. Stemper on Tue Dec 13 15:03:10 2022
    "Michael F. Stemper" <michael.stemper@gmail.com> writes:
    def fred(cf,toplevel=True):
    x = cf[0]
    if len(cf)>1:
    if toplevel:
    return x + fred(cf[1:],False)
    else:
    return "(" + x + fred(cf[1:],False) + ")"
    else:
    if toplevel:
    return x
    else:
    return "(" + x + ")"

    def rest( s ):
    return "(" + s[ 0 ] +( rest( s[1:] ) if len( s )> 1 else '' )+ ')'

    def nest( s ):
    return( s[ 0 ] if s else '' )+( rest( s[1:] )if len( s )> 1 else '' )

    fred = nest

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael F. Stemper@21:1/5 to All on Tue Dec 13 08:46:29 2022
    It's easy enough -- in fact necessary -- to handle the bottom
    level of a function differently than the levels above it. What
    about the case where you want to handle something differently
    in the top level than in lower levels? Is there any way to tell
    from within a function that it wasn't invoked by itself?

    I've come up with a hack to support different processing, by
    use of an extra argument, as shown in this simplified example:

    def fred(cf,toplevel=True):
    x = cf[0]
    if len(cf)>1:
    if toplevel:
    return x + fred(cf[1:],False)
    else:
    return "(" + x + fred(cf[1:],False) + ")"
    else:
    if toplevel:
    return x
    else:
    return "(" + x + ")"

    Aside from being ugly, this lets the caller diddle with "toplevel",
    which shouldn't really be externally modifiable.

    Are there better ways to do this?
    --
    Michael F. Stemper
    I refuse to believe that a corporation is a person until Texas executes one.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julio Di Egidio@21:1/5 to Michael F. Stemper on Tue Dec 13 07:35:34 2022
    On Tuesday, 13 December 2022 at 15:46:49 UTC+1, Michael F. Stemper wrote:

    about the case where you want to handle something differently
    in the top level than in lower levels? Is there any way to tell
    from within a function that it wasn't invoked by itself?

    I've come up with a hack to support different processing, by
    use of an extra argument
    def fred(cf,toplevel=True):

    To make it slightly more general pass the level number,
    then it's quite a common scheme.

    Aside from being ugly, this lets the caller diddle with "toplevel",
    which shouldn't really be externally modifiable.

    That's a different issue, that Python is "strictly dynamic"
    (there is no such thing as constants or private scope):
    which one can "mitigate", courtesy supporting tools,
    by decorating constructs with type annotations.

    Julio

    Are there better ways to do this?
    --
    Michael F. Stemper
    I refuse to believe that a corporation is a person until Texas executes one.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Stefan Ram on Tue Dec 13 15:25:08 2022
    Supersedes: <reduce-20221213161758@ram.dialup.fu-berlin.de>

    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    def rest( s ):
    return "(" + s[ 0 ] +( rest( s[1:] ) if len( s )> 1 else '' )+ ')'
    def nest( s ):
    return( s[ 0 ] if s else '' )+( rest( s[1:] )if len( s )> 1 else '' )

    Below, I have tried to reduce repetitiveness a bit.

    (PS: Now, one "if" remains; less ifs are not possible
    in the case of controlled recursion.)

    def rest( s ):
    return '(' + nest( s )+ ')'

    def nest( s ):
    return s[ :1 ]+( rest( s[ 1: ])if s[ 1: ]else '' )

    fred = nest

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julio Di Egidio@21:1/5 to Julio Di Egidio on Tue Dec 13 07:43:57 2022
    On Tuesday, 13 December 2022 at 16:35:48 UTC+1, Julio Di Egidio wrote:
    On Tuesday, 13 December 2022 at 15:46:49 UTC+1, Michael F. Stemper wrote:

    about the case where you want to handle something differently
    in the top level than in lower levels? Is there any way to tell
    from within a function that it wasn't invoked by itself?

    I've come up with a hack to support different processing, by
    use of an extra argument
    def fred(cf,toplevel=True):

    To make it slightly more general pass the level number,
    then it's quite a common scheme.

    Aside from being ugly, this lets the caller diddle with "toplevel",
    which shouldn't really be externally modifiable.

    That's a different issue, that Python is "strictly dynamic"
    (there is no such thing as constants or private scope):
    which one can "mitigate", courtesy supporting tools,
    by decorating constructs with type annotations.

    The function calls itself, anyway indeed another issue is
    that the "original caller" could pass false, which is where
    the usual pattern is to expose the straight interface to
    the user, and that in turn starts the recursion (and DO
    NOT use argument defaults, leave it explicit):

    def fred(cf):
    return fred_(cf, True)

    def fred_(cf, toplevel):
    ...

    HTH,

    Julio

    Are there better ways to do this?
    --
    Michael F. Stemper
    I refuse to believe that a corporation is a person until Texas executes one.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Stefan Ram on Tue Dec 13 17:06:52 2022
    ram@zedat.fu-berlin.de (Stefan Ram) writes:
    def rest( s ):
    return '(' + nest( s )+ ')'
    def nest( s ):
    return s[ :1 ]+( rest( s[ 1: ])if s[ 1: ]else '' )

    Of course, these can now be combined into a single recursive
    function which does not need a special case handling for the
    first call anymore.

    def fred( s ):
    return s[ :1 ]+( ( '(' + fred( s[ 1: ] )+ ')' )if s[ 1: ]else '' )

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mats Wichmann@21:1/5 to Chris Angelico on Tue Dec 13 10:57:59 2022
    On 12/13/22 10:36, Chris Angelico wrote:
    On Wed, 14 Dec 2022 at 03:35, Michael F. Stemper
    <michael.stemper@gmail.com> wrote:

    It's easy enough -- in fact necessary -- to handle the bottom
    level of a function differently than the levels above it. What
    about the case where you want to handle something differently
    in the top level than in lower levels? Is there any way to tell
    from within a function that it wasn't invoked by itself?


    Why does it have to be the same function?

    def _sort_recursive(stuff, keys, start, end):
    """imagine a nice implementation of some sorting algorithm here"""

    def sort(stuff, key=None):
    if key:
    keys = [key(x) for x in stuff]
    else:
    keys = stuff
    return _sort_recursive(stuff, 0, len(stuff))

    if some support for this position is needed, this is roughly how the
    stdlib glob() function is implemented.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to Mats Wichmann on Wed Dec 14 05:06:04 2022
    On Wed, 14 Dec 2022 at 05:01, Mats Wichmann <mats@wichmann.us> wrote:

    On 12/13/22 10:36, Chris Angelico wrote:
    On Wed, 14 Dec 2022 at 03:35, Michael F. Stemper <michael.stemper@gmail.com> wrote:

    It's easy enough -- in fact necessary -- to handle the bottom
    level of a function differently than the levels above it. What
    about the case where you want to handle something differently
    in the top level than in lower levels? Is there any way to tell
    from within a function that it wasn't invoked by itself?


    Why does it have to be the same function?

    def _sort_recursive(stuff, keys, start, end):
    """imagine a nice implementation of some sorting algorithm here"""

    def sort(stuff, key=None):
    if key:
    keys = [key(x) for x in stuff]
    else:
    keys = stuff
    return _sort_recursive(stuff, 0, len(stuff))

    if some support for this position is needed, this is roughly how the
    stdlib glob() function is implemented.


    Yeah, lots of things are done that way. You'll find a variety of
    naming conventions around different languages and libraries, including "_low_FUNCTIONNAME" or "_internal_FUNCTIONNAME" etc. It's definitely
    easier than trying to mess with tracking toplevel status.

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to michael.stemper@gmail.com on Wed Dec 14 04:36:46 2022
    On Wed, 14 Dec 2022 at 03:35, Michael F. Stemper
    <michael.stemper@gmail.com> wrote:

    It's easy enough -- in fact necessary -- to handle the bottom
    level of a function differently than the levels above it. What
    about the case where you want to handle something differently
    in the top level than in lower levels? Is there any way to tell
    from within a function that it wasn't invoked by itself?


    Why does it have to be the same function?

    def _sort_recursive(stuff, keys, start, end):
    """imagine a nice implementation of some sorting algorithm here"""

    def sort(stuff, key=None):
    if key:
    keys = [key(x) for x in stuff]
    else:
    keys = stuff
    return _sort_recursive(stuff, 0, len(stuff))

    With purely recursive functions (where every call to the function
    truly could have been a top-level call - a lot of mathematical
    functions work out this way), it makes sense to call the
    externally-callable function recursively; but for anything more messy,
    it's usually easiest to make the recursive part internal, and then
    have a top-level function that calls into the recursive one.

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Schachner, Joseph (US)@21:1/5 to All on Tue Dec 13 17:19:07 2022
    UmVkdWNpbmcgcmVwZXRpdGl2ZW5lc3MgaGFzIG1hZGUgdGhpcyBjb2RlIGhhcmRlciB0byByZWFk LiBJIGhhZCB0byB0aGluayBhYm91dCB3aGF0IGl0IGlzIGRvaW5nLiAgSXQgbWlnaHQgYmUgc2xp Z2h0bHkgZmFzdGVyLCBidXQgaW4gbXkgb3BpbmlvbiBpdCBpcyBub3Qgd29ydGggaXQuICANCg0K LS0tIEpvc2VwaCBTLg0KDQoNClRlbGVkeW5lIENvbmZpZGVudGlhbDsgQ29tbWVyY2lhbGx5IFNl bnNpdGl2ZSBCdXNpbmVzcyBEYXRhDQoNCi0tLS0tT3JpZ2luYWwgTWVzc2FnZS0tLS0tDQpGcm9t OiBTdGVmYW4gUmFtIDxyYW1AemVkYXQuZnUtYmVybGluLmRlPiANClNlbnQ6IFR1ZXNkYXksIERl Y2VtYmVyIDEzLCAyMDIyIDEwOjI1IEFNDQpUbzogcHl0aG9uLWxpc3RAcHl0aG9uLm9yZw0KU3Vi amVjdDogUmU6IFRvcCBsZXZlbCBvZiBhIHJlY3Vyc2l2ZSBmdW5jdGlvbg0KDQpTdXBlcnNlZGVz OiA8cmVkdWNlLTIwMjIxMjEzMTYxNzU4QHJhbS5kaWFsdXAuZnUtYmVybGluLmRlPg0KDQpyYW1A emVkYXQuZnUtYmVybGluLmRlIChTdGVmYW4gUmFtKSB3cml0ZXM6DQo+ZGVmIHJlc3QoIHMgKToN Cj4gICAgcmV0dXJuICIoIiArIHNbIDAgXSArKCByZXN0KCBzWzE6XSApIGlmIGxlbiggcyApPiAx IGVsc2UgJycgKSsgJyknDQo+ZGVmIG5lc3QoIHMgKToNCj4gICAgcmV0dXJuKCBzWyAwIF0gaWYg cyBlbHNlICcnICkrKCByZXN0KCBzWzE6XSApaWYgbGVuKCBzICk+IDEgZWxzZSAnJyApDQoNCiAg QmVsb3csIEkgaGF2ZSB0cmllZCB0byByZWR1Y2UgcmVwZXRpdGl2ZW5lc3MgYSBiaXQuDQoNCiAg KFBTOiBOb3csIG9uZSAiaWYiIHJlbWFpbnM7IGxlc3MgaWZzIGFyZSBub3QgcG9zc2libGUNCiAg aW4gdGhlIGNhc2Ugb2YgY29udHJvbGxlZCByZWN1cnNpb24uKQ0KDQpkZWYgcmVzdCggcyApOg0K ICAgIHJldHVybiAnKCcgKyBuZXN0KCBzICkrICcpJw0KDQpkZWYgbmVzdCggcyApOg0KICAgIHJl dHVybiBzWyA6MSBdKyggcmVzdCggc1sgMTogXSlpZiBzWyAxOiBdZWxzZSAnJyApDQoNCmZyZWQg PSBuZXN0DQoNCg0K

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael F. Stemper@21:1/5 to Stefan Ram on Tue Dec 13 14:28:26 2022
    On 13/12/2022 09.03, Stefan Ram wrote:
    "Michael F. Stemper" <michael.stemper@gmail.com> writes:
    def fred(cf,toplevel=True):
    x = cf[0]
    if len(cf)>1:
    if toplevel:
    return x + fred(cf[1:],False)
    else:
    return "(" + x + fred(cf[1:],False) + ")"
    else:
    if toplevel:
    return x
    else:
    return "(" + x + ")"

    def rest( s ):
    return "(" + s[ 0 ] +( rest( s[1:] ) if len( s )> 1 else '' )+ ')'

    def nest( s ):
    return( s[ 0 ] if s else '' )+( rest( s[1:] )if len( s )> 1 else '' )

    Hey, that's slick! And if I define rest within nest, it's not going
    to be externally accessible.

    Thanks

    --
    Michael F. Stemper
    No animals were harmed in the composition of this message.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julio Di Egidio@21:1/5 to Chris Angelico on Wed Dec 14 01:58:40 2022
    On Tuesday, 13 December 2022 at 18:37:20 UTC+1, Chris Angelico wrote:
    On Wed, 14 Dec 2022 at 03:35, Michael F. Stemper
    <michael...@gmail.com> wrote:

    It's easy enough -- in fact necessary -- to handle the bottom
    level of a function differently than the levels above it. What
    about the case where you want to handle something differently
    in the top level than in lower levels? Is there any way to tell
    from within a function that it wasn't invoked by itself?

    Why does it have to be the same function?

    def _sort_recursive(stuff, keys, start, end):
    """imagine a nice implementation of some sorting algorithm here"""

    def sort(stuff, key=None):
    if key:
    keys = [key(x) for x in stuff]
    else:
    keys = stuff
    return _sort_recursive(stuff, 0, len(stuff))

    With purely recursive functions (where every call to the function
    truly could have been a top-level call - a lot of mathematical
    functions work out this way), it makes sense to call the
    externally-callable function recursively; but for anything more messy,
    it's usually easiest to make the recursive part internal, and then
    have a top-level function that calls into the recursive one.

    Thanks for always posting on top of any decent answers
    with your half-cooked incompetent fraudulent bullshit.

    Julio

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julio Di Egidio@21:1/5 to Michael F. Stemper on Wed Dec 14 02:05:27 2022
    On Tuesday, 13 December 2022 at 21:28:47 UTC+1, Michael F. Stemper wrote:
    On 13/12/2022 09.03, Stefan Ram wrote:
    "Michael F. Stemper" <michael...@gmail.com> writes:
    def fred(cf,toplevel=True):
    x = cf[0]
    if len(cf)>1:
    if toplevel:
    return x + fred(cf[1:],False)
    else:
    return "(" + x + fred(cf[1:],False) + ")"
    else:
    if toplevel:
    return x
    else:
    return "(" + x + ")"

    def rest( s ):
    return "(" + s[ 0 ] +( rest( s[1:] ) if len( s )> 1 else '' )+ ')'

    def nest( s ):
    return( s[ 0 ] if s else '' )+( rest( s[1:] )if len( s )> 1 else '' )

    Hey, that's slick! And if I define rest within nest, it's not going
    to be externally accessible.

    When Dunning-Kruger is a compliment: under pretty
    much every respect that is the opposite of anything
    a sane programmer would write at any level...

    Indeed, while Angelico is the typical fraudulent
    programmer, you and Ram are the typical retarded
    math sheriffs. He's lucky I don't see his posts,
    the fucking fraud...

    Get extinguished already.

    Julio

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Michael F. Stemper on Wed Dec 14 13:49:38 2022
    "Michael F. Stemper" <michael.stemper@gmail.com> writes:
    Hey, that's slick! And if I define rest within nest, it's not going
    to be externally accessible.

    Thank you! Today, I'd write my final version of yesterday
    with some more names to increase readability:

    def parenthesized( s ):
    return '(' + s + ')'

    def fred( s ):
    head = s[ :1 ]
    rest = s[ 1: ]
    tail = parenthesized( fred( rest ))if rest else ''
    return head + tail

    . This also works when s is empty, returning the empty string.

    I also found an example similar to what was discussed here
    in pypy's library file "...\Lib\_tkinter\__init__.py":

    |def _flatten(item):
    | def _flatten1(output, item, depth):
    | if depth > 1000:
    | raise ValueError("nesting too deep in _flatten")
    | if not isinstance(item, (list, tuple)):
    | raise TypeError("argument must be sequence")
    | # copy items to output tuple
    | for o in item:
    | if isinstance(o, (list, tuple)):
    | _flatten1(output, o, depth + 1)
    | elif o is not None:
    | output.append(o)
    |
    | result = []
    | _flatten1(result, item, 0)
    | return tuple(result)

    .

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Peter Otten@21:1/5 to Michael F. Stemper on Thu Dec 15 09:39:54 2022
    On 13/12/2022 15:46, Michael F. Stemper wrote:
    It's easy enough -- in fact necessary -- to handle the bottom
    level of a function differently than the levels above it. What
    about the case where you want to handle something differently
    in the top level than in lower levels? Is there any way to tell
    from within a function that it wasn't invoked by itself?

    I've come up with a hack to support different processing, by
    use of an extra argument, as shown in this simplified example:

    def fred(cf,toplevel=True):
      x = cf[0]
      if len(cf)>1:
        if toplevel:
          return x + fred(cf[1:],False)
        else:
          return "(" + x + fred(cf[1:],False) + ")"
      else:
        if toplevel:
          return x
        else:
          return "(" + x + ")"

    Aside from being ugly, this lets the caller diddle with "toplevel",
    which shouldn't really be externally modifiable.

    Are there better ways to do this?

    For adepts of functional programming the above is a "fold right"
    operation, first hit for "foldr in python":

    https://burgaud.com/foldl-foldr-python

    With that

    from functools import reduce
    def foldr(func, items):
    return reduce(lambda x, y: func(y, x), items[::-1])

    your problem reduces ;) to

    foldr("{0}({1})".format, [1,2,3,4])
    '1(2(3(4)))'

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julio Di Egidio@21:1/5 to Peter Otten on Thu Dec 15 05:15:29 2022
    On Thursday, 15 December 2022 at 09:57:52 UTC+1, Peter Otten wrote:
    On 13/12/2022 15:46, Michael F. Stemper wrote:

    I've come up with a hack to support different processing, by
    use of an extra argument, as shown in this simplified example:

    def fred(cf,toplevel=True):
    x = cf[0]
    if len(cf)>1:
    if toplevel:
    return x + fred(cf[1:],False)
    else:
    return "(" + x + fred(cf[1:],False) + ")"
    else:
    if toplevel:
    return x
    else:
    return "(" + x + ")"

    Aside from being ugly, this lets the caller diddle with "toplevel",
    which shouldn't really be externally modifiable.

    Are there better ways to do this?

    For adepts of functional programming the above is a "fold right"
    operation, first hit for "foldr in python":

    https://burgaud.com/foldl-foldr-python

    With that

    from functools import reduce
    def foldr(func, items):

    return reduce(lambda x, y: func(y, x), items[::-1])

    Sure, an easy immediate generalization, but arguably
    more cogent was to think "passing state/context into
    pure functions", which is what I have been hinting at in
    my opening answer, and *that*'s actually functional
    programming, not just maps and folds...

    HTH,

    Julio

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rob Cliffe@21:1/5 to Stefan Ram on Wed Dec 14 19:17:50 2022
    On 14/12/2022 13:49, Stefan Ram wrote:
    I also found an example similar to what was discussed here
    in pypy's library file "...\Lib\_tkinter\__init__.py":

    |def _flatten(item):
    | def _flatten1(output, item, depth):
    | if depth > 1000:
    | raise ValueError("nesting too deep in _flatten")
    | if not isinstance(item, (list, tuple)):
    | raise TypeError("argument must be sequence")
    | # copy items to output tuple
    | for o in item:
    | if isinstance(o, (list, tuple)):
    | _flatten1(output, o, depth + 1)
    | elif o is not None:
    | output.append(o)
    |
    | result = []
    | _flatten1(result, item, 0)
    | return tuple(result)

    .


    This presumably results in an (avoidable) run-time overhead from
    constructing _flatten1 every time _flatten is called (and having it garbage-collected later).
    Best wishes
    Rob Cliffe

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)