accumulator generators

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Cameron

    accumulator generators

    I was reading this <a href="this http://www.paulgraham. com/icad.html">Paul
    Graham article</aand he builds an accumuator generator function in
    the appendix. His looks like this:

    <pre>
    def foo(n):
    s = [n]
    def bar(i):
    s[0] += i
    return s[0]
    return bar
    </pre>

    Why does that work, but not this:

    <pre>
    def foo(n):
    s = n
    def bar(i):
    s += i
    return s
    return bar
    </pre>
  • Diez B. Roggisch

    #2
    Re: accumulator generators

    Cameron schrieb:
    I was reading this <a href="this http://www.paulgraham. com/icad.html">Paul
    Graham article</aand he builds an accumuator generator function in
    the appendix. His looks like this:
    >
    <pre>
    def foo(n):
    s = [n]
    def bar(i):
    s[0] += i
    return s[0]
    return bar
    </pre>
    >
    Why does that work, but not this:
    >
    <pre>
    def foo(n):
    s = n
    def bar(i):
    s += i
    return s
    return bar
    </pre>
    Because python's static analysis infers s as being a variable local to
    bar in the second case - so you can't modify it in the outer scope.

    In the future, you may declare

    def bar(i):
    nonlocal s
    ...


    Diez

    Comment

    • Kirk Strauser

      #3
      Re: accumulator generators

      At 2008-05-30T19:50:43Z, Cameron <cameronlarue@g mail.comwrites:
      Why does that work, but not this:
      >
      def foo(n):
      s = n
      def bar(i):
      s += i
      return s
      return bar
      Assume that n is an int, making s one also. Ints are immutable; you can
      only copy them. So your bar is taking s, adding i to it, then assigning the
      value back to the local variable named s.
      --
      Kirk Strauser
      The Day Companies

      Comment

      • Cameron

        #4
        Re: accumulator generators

        On May 30, 1:04 pm, "Diez B. Roggisch" <de...@nospam.w eb.dewrote:
        Cameron schrieb:
        >
        >
        >
        I was reading this <a href="thishttp://www.paulgraham. com/icad.html">Paul
        Graham article</aand he builds an accumuator generator function in
        the appendix. His looks like this:
        >
        <pre>
        def foo(n):
          s = [n]
          def bar(i):
            s[0] += i
            return s[0]
          return bar
        </pre>
        >
        Why does that work, but not this:
        >
        <pre>
        def foo(n):
          s = n
          def bar(i):
            s += i
            return s
          return bar
        </pre>
        >
        Because python's static analysis infers s as being a variable local to
        bar in the second case - so you can't modify it in the outer scope.
        >
        In the future, you may declare
        >
        def bar(i):
             nonlocal s
             ...
        >
        Diez
        thanks for the response. Just to make sure I understand- Is the reason
        it works in the first case because s[0] is undefined at that point (in
        bar), and so python looks in the outer scope and finds it there?

        Cameron

        Comment

        • Hans Nowak

          #5
          Re: accumulator generators

          Cameron wrote:
          On May 30, 1:04 pm, "Diez B. Roggisch" <de...@nospam.w eb.dewrote:
          >Cameron schrieb:
          >>
          >>
          >>
          >>I was reading this <a href="thishttp://www.paulgraham. com/icad.html">Paul
          >>Graham article</aand he builds an accumuator generator function in
          >>the appendix. His looks like this:
          >><pre>
          >>def foo(n):
          >> s = [n]
          >> def bar(i):
          >> s[0] += i
          >> return s[0]
          >> return bar
          >></pre>
          >>Why does that work, but not this:
          >><pre>
          >>def foo(n):
          >> s = n
          >> def bar(i):
          >> s += i
          >> return s
          >> return bar
          >></pre>
          >Because python's static analysis infers s as being a variable local to
          >bar in the second case - so you can't modify it in the outer scope.
          >>
          >In the future, you may declare
          >>
          >def bar(i):
          > nonlocal s
          > ...
          >>
          >Diez
          >
          thanks for the response. Just to make sure I understand- Is the reason
          it works in the first case because s[0] is undefined at that point (in
          bar), and so python looks in the outer scope and finds it there?
          You can refer to variables in enclosing scopes, just not redefine them in that
          same scope. That's why in the first example, bar can refer to to s (defined in
          foo). By assigning to s[0], it modifies the list, which is OK; trying to
          redefine the name 's' (like the second example tries to do) would not be OK.

          Also see: http://zephyrfalcon.org/labs/python_pitfalls.html (pitfall #6).

          --
          Hans Nowak (zephyrfalcon at gmail dot com)

          Comment

          • Arnaud Delobelle

            #6
            Re: accumulator generators

            Cameron <cameronlarue@g mail.comwrites:
            I was reading this <a href="this http://www.paulgraham. com/icad.html">Paul
            Graham article</aand he builds an accumuator generator function in
            the appendix. His looks like this:
            >
            <pre>
            def foo(n):
            s = [n]
            def bar(i):
            s[0] += i
            return s[0]
            return bar
            </pre>
            >
            Why does that work, but not this:
            >
            <pre>
            def foo(n):
            s = n
            def bar(i):
            s += i
            return s
            return bar
            </pre>
            Others have explained why, but this looks like "pythonized LISP" to
            me. I would rather use a generator function:

            def foo(n):
            while True:
            n += yield n

            Although the problem is that you can't send it values the first time
            round!
            >>bar = foo('s')
            >>bar.next()
            's'
            >>bar.send('p ')
            'sp'
            >>bar.send('am' )
            'spam'

            But:
            >>bar = foo(3)
            >>bar.send(2)
            Traceback (most recent call last):
            File "<stdin>", line 1, in <module>
            TypeError: can't send non-None value to a just-started generator
            >>>
            --
            Arnaud

            Comment

            • George Sakkis

              #7
              Re: accumulator generators

              On May 31, 4:19 am, Arnaud Delobelle <arno...@google mail.comwrote:
              Cameron <cameronla...@g mail.comwrites:
              I was reading this <a href="thishttp://www.paulgraham. com/icad.html">Paul
              Graham article</aand he builds an accumuator generator function in
              the appendix. His looks like this:
              >
              <pre>
              def foo(n):
                s = [n]
                def bar(i):
                  s[0] += i
                  return s[0]
                return bar
              </pre>
              >
              Why does that work, but not this:
              >
              <pre>
              def foo(n):
                s = n
                def bar(i):
                  s += i
                  return s
                return bar
              </pre>
              >
              Others have explained why, but this looks like "pythonized LISP" to
              me.  I would rather use a generator function:
              >
              def foo(n):
                  while True:
                      n += yield n
              >
              Although the problem is that you can't send it values the first time
              round!
              >
              >bar = foo('s')
              >bar.next()
              's'
              >bar.send('p' )
              'sp'
              >bar.send('am ')
              >
              'spam'
              >
              But:
              >
              >bar = foo(3)
              >bar.send(2)
              >
              Traceback (most recent call last):
                File "<stdin>", line 1, in <module>
              TypeError: can't send non-None value to a just-started generator
              I find the "pythonized LISP" solution more understandable, even
              without the initial next() requirement. YMMV

              George

              Comment

              • Arnaud Delobelle

                #8
                Re: accumulator generators

                George Sakkis <george.sakkis@ gmail.comwrites :
                On May 31, 4:19 am, Arnaud Delobelle <arno...@google mail.comwrote:
                >Cameron <cameronla...@g mail.comwrites:
                I was reading this <a href="thishttp://www.paulgraham. com/icad.html">Paul
                Graham article</aand he builds an accumuator generator function in
                the appendix. His looks like this:
                >>
                <pre>
                def foo(n):
                  s = [n]
                  def bar(i):
                    s[0] += i
                    return s[0]
                  return bar
                </pre>
                >>
                Why does that work, but not this:
                >>
                <pre>
                def foo(n):
                  s = n
                  def bar(i):
                    s += i
                    return s
                  return bar
                </pre>
                >>
                >Others have explained why, but this looks like "pythonized LISP" to
                >me.  I would rather use a generator function:
                >>
                >def foo(n):
                >    while True:
                >        n += yield n
                >>
                >Although the problem is that you can't send it values the first time
                >round!
                >>
                >>bar = foo('s')
                >>bar.next()
                >'s'
                >>bar.send('p ')
                >'sp'
                >>bar.send('am' )
                >>
                >'spam'
                >>
                >But:
                >>
                >>bar = foo(3)
                >>bar.send(2)
                >>
                >Traceback (most recent call last):
                >  File "<stdin>", line 1, in <module>
                >TypeError: can't send non-None value to a just-started generator
                >
                I find the "pythonized LISP" solution more understandable, even
                without the initial next() requirement. YMMV
                >
                George
                In that case a class may be better? IMHO, what is such a natural
                idiom in LISP does not translate well literally into Python.

                class Foo(object):
                def __init__(self, n):
                self.s = n
                def __call__(self, i)
                self.s += i
                return self.s

                Anything to avoid the mutable container trick! Of course 'nonlocal'
                takes care of this in py3k. I have missed the absence of 'nonlocal' a
                lot, but now that it is around the corner, I grow less sure about it.

                --
                Arnaud

                Comment

                Working...