Factory functions: synthesizing top-level names

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Factory functions: synthesizing top-level names

kirby urner-4
Creating Top-Level names root0, root1, root2... rootN

Setting the stage:

We've come across an ethnicity, in our voyages
of the starship whatever, that doesn't share our
fascination with squares and cubes.  For example,
when multiplying n * n, it never occurred to them
to picture n * n squares in a checkerboard pattern.
Rather, they have a more Chinese Checkers bias
towards triangles and hexagons, and their picture
of n * n is an equilateral triangle subdivided into
n ** 2 similar sub-triangles.  

Likewise, their picture of n * n * n, or n**3 is a 
tetrahedron, though here the subdividing process 
results in a matrix known to Earthlings ("terra-ists") 
as the FCC and/or CCP and/or... whatever Earthling
terminology.

The Challenge:

The anthropologist-astronauts we've left behind
to work and study with these people (to be 
collected on a next pass a year from now, unless
they plan on staying longer), are using Python, and
find that saying 'sqrt' just gets in the way (keeps 
them habituated to patterns of thought they're trying
to snap out of).  

What they'd like to do is this:  for 2nd, 3rd, 4th, 
5th etc. root of a number, have the functions 
root2, root3, root4... up to root10 routinely available, 
perhaps in a module called roots or radicals.

>>> import radicals as r
>>> r.root3 ( 2.0 / 3.0 )
0.8735804647362989

So how might we generate root0... root10 in a 
factory function, such that these function names
become top level within their generating and/or 
defining module?  

True, we could individually define these functions 
per the example below, but what is a clever way 
of generating rootN in principle, such as root2012,
taking 2012 as an argument.

Hints:

For root2, you might take an obvious shortcut:

from math import sqrt as root2

For root3, consider the following:

def root3 (x):  return pow( x, 1.0/3.0 )

In general:

def rootN (x):  return pow( x, 1.0/N)

Rationale for this exercise:

Students often want a way to factory-produce
new names at the top level, from within a contained
scope (such as from within a function).  

These top level names may be of data, functions or 
whatever arbitrary objects.  The production process 
often involves synthesizing new names on the fly, 
as strings.  Strings are "right side" objects, usually 
assigned names, but not themselves names of 
anything.

By what magic might we turn a string object into 
a top-level name:  that is the question this lesson 
addresses.


_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Factory functions: synthesizing top-level names

kirby urner-4
> By what magic might we turn a string object into
> a top-level name:  that is the question this lesson
> addresses.

I do have an answer in the queue, my proposed solution.

However I'd be happy to see others toss in some ideas,
just to test whether my lesson plan is clear enough to
get across what the challenge is.

Maybe not.

On another front, I'm wanting to tell the story of free
and open source software to a next generation of teen,
and wondered if FOSS veterans here would have any
feedback regarding the following thumbnail:

<LORE>

GNU = GNU is not Unix and was the original inspiration for the
free software movement, centered on what become known as
the GNU Public License (GPL).

The GPL was a way for copyright holders to insist that their hard
work not be used only selfishly by others, that the fruits of their
labors achieve maximum positive benefit for omni-humanity, to
put a Global U spin on it.

Once the GPL was out there getting the work done (e.g. Linux),
a safe-enough ecosystem was developed for a bevy of alternative
licensing schemes, some of which allow shops to take something
inhouse, transform it, and release it under a more restrictive
license agreement, if they release it at all.

[ Python could be an example of this, in that developers of the
IronPython codebase have been enabled to start with CPython,
the original version, with the resulting codebase being free and
open source under a different set of rules from the GPL or even
from whatever the original Python was using.  Actually, the story
is more complicated than that, in that Jim H. first worked on the
Java implementation before moving to C# in an attempt to prove
the CLR (common language runtime) engine was unsuitable for
dynamic languages.  He came to the conclusion that the CLR
engine could work, hence the IronPython project. ]

</LORE>

The original context is some Google group (a new one).  The
relevant thread is accessible from this blog post FYI:

http://mybizmo.blogspot.com/2010/05/math-reform.html

Where it says:
Rhombic Triacontahedra etc. (Synergetics list) -- Urner

(I'm still adding to this list of May 2010 math reform writings).

Kirby
_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Factory functions: synthesizing top-level names

André Roberge
In reply to this post by kirby urner-4


On Wed, May 26, 2010 at 4:58 PM, kirby urner <[hidden email]> wrote:
Creating Top-Level names root0, root1, root2... rootN

SNIP
 

What they'd like to do is this:  for 2nd, 3rd, 4th, 
5th etc. root of a number, have the functions 
root2, root3, root4... up to root10 routinely available, 
perhaps in a module called roots or radicals.

>>> import radicals as r
>>> r.root3 ( 2.0 / 3.0 )
0.8735804647362989

So how might we generate root0... root10 in a 
factory function, such that these function names
become top level within their generating and/or 
defining module?  

True, we could individually define these functions 
per the example below, but what is a clever way 
of generating rootN in principle, such as root2012,
taking 2012 as an argument.


Ok, here's my solution:

>>> def make_root(i):
...    def _root(x):
...         return pow(x, 1.0/i)
...    return _root
...
>>> for i in range(1, 11):
...     locals()['root' + str(i)] = make_root(i)
...
>>> root2(4)
2.0
>>> root4(64)
2.8284271247461903
>>> root6(64)
2.0

Cheers,
André

 
Hints:

For root2, you might take an obvious shortcut:

from math import sqrt as root2

For root3, consider the following:

def root3 (x):  return pow( x, 1.0/3.0 )

In general:

def rootN (x):  return pow( x, 1.0/N)

Rationale for this exercise:

Students often want a way to factory-produce
new names at the top level, from within a contained
scope (such as from within a function).  

These top level names may be of data, functions or 
whatever arbitrary objects.  The production process 
often involves synthesizing new names on the fly, 
as strings.  Strings are "right side" objects, usually 
assigned names, but not themselves names of 
anything.

By what magic might we turn a string object into 
a top-level name:  that is the question this lesson 
addresses.


_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig



_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Factory functions: synthesizing top-level names

kirby urner-4
On Wed, May 26, 2010 at 7:21 PM, Andre Roberge <[hidden email]> wrote:

>
> Ok, here's my solution:
>
>>>> def make_root(i):
> ...    def _root(x):
> ...         return pow(x, 1.0/i)
> ...    return _root
> ...
>>>> for i in range(1, 11):
> ...     locals()['root' + str(i)] = make_root(i)
> ...
>>>> root2(4)
> 2.0
>>>> root4(64)
> 2.8284271247461903
>>>> root6(64)
> 2.0
>
> Cheers,
> André
>

Much appreciated André!

I think our solutions are similar:

"""
http://mail.python.org/pipermail/edu-sig/2010-May/009998.html
http://mail.python.org/pipermail/edu-sig/2010-May/009999.html
"""

def makeroot(N):
    try:
        assert type(N)==type(1) and N>=0
    except:
        raise ValueError("0 <= N <= integer")

    fname = "root" + str(N)

    if N==0:
        fdef = "globals()['%s'] = lambda x: pow(x, 0)" % fname
    else:
        fdef = "globals()['%s'] = lambda x: pow(x, float(1)/%s)" % (fname,N)

    exec(fdef)

Kirby
_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Factory functions: synthesizing top-level names

kirby urner-4
>
> Much appreciated André!
>
> I think our solutions are similar:
>

... yours is better though, in that you don't make use of exec.

My use of it was superfluous.

Changing my solution, in light of yours:

#===

def makeroot(N):
   try:
       assert type(N)==type(1) and N>=0
   except:
       raise ValueError("0 <= N <= integer")

   fname = "root" + str(N)

   if N==0:
       globals()[fname] = lambda x: pow(x, 0)
   else:
       globals()[fname] = lambda x: pow(x, float(1)/N)


for i in range(11):
    makeroot(i)

#===

Kirby
_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Factory functions: synthesizing top-level names

André Roberge


On Thu, May 27, 2010 at 2:52 AM, kirby urner <[hidden email]> wrote:
>
> Much appreciated André!

It was a fun challenge.
 :-)
>
> I think our solutions are similar:
>

... yours is better though, in that you don't make use of exec.

My use of it was superfluous.

Changing my solution, in light of yours:

#===

def makeroot(N):
  try:
      assert type(N)==type(1) and N>=0
  except:
      raise ValueError("0 <= N <= integer")

  fname = "root" + str(N)

  if N==0:
      globals()[fname] = lambda x: pow(x, 0)
  else:
      globals()[fname] = lambda x: pow(x, float(1)/N)


for i in range(11):
   makeroot(i)

#===


Technically, your use of globals() instead of locals(), like I did, is better, in that it allows you to have it inside a function body, and still be available at the module level, as per your stated goal.

However, I find the use of lambda to be too restrictive, in principle, as it obviously works only for expressions, and not complex functions.  If this example is to be used as a prototype to show students how to do this kind of name assignment, then the way I have done it with an inner named function (which I first learned about *years* ago, from a post from you on edu-sig!  I believe it was http://mail.python.org/pipermail/edu-sig/2005-March/004590.html) is a better way to go.
 
Kirby


_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig
Reply | Threaded
Open this post in threaded view
|

Re: Factory functions: synthesizing top-level names

kirby urner-4
On Thu, May 27, 2010 at 3:15 AM, Andre Roberge <[hidden email]> wrote:

>
>
> On Thu, May 27, 2010 at 2:52 AM, kirby urner <[hidden email]> wrote:
>>
>> >
>> > Much appreciated André!
>
> It was a fun challenge.
>  :-)
>>
>> >
>> > I think our solutions are similar:
>> >
>>
>> ... yours is better though, in that you don't make use of exec.
>>
>> My use of it was superfluous.
>>
>> Changing my solution, in light of yours:
>>
>> #===
>>
>> def makeroot(N):
>>   try:
>>       assert type(N)==type(1) and N>=0
>>   except:
>>       raise ValueError("0 <= N <= integer")
>>
>>   fname = "root" + str(N)
>>
>>   if N==0:
>>       globals()[fname] = lambda x: pow(x, 0)
>>   else:
>>       globals()[fname] = lambda x: pow(x, float(1)/N)
>>
>>
>> for i in range(11):
>>    makeroot(i)
>>
>> #===
>>
>
> Technically, your use of globals() instead of locals(), like I did, is
> better, in that it allows you to have it inside a function body, and still
> be available at the module level, as per your stated goal.
>
> However, I find the use of lambda to be too restrictive, in principle, as it
> obviously works only for expressions, and not complex functions.  If this
> example is to be used as a prototype to show students how to do this kind of
> name assignment, then the way I have done it with an inner named function
> (which I first learned about *years* ago, from a post from you on edu-sig!
> I believe it was
> http://mail.python.org/pipermail/edu-sig/2005-March/004590.html) is a better
> way to go.
>
>>
>> Kirby
>

Yes sir, I agree.  The Python lambda is weak (on purpose) in not providing
for full scale anonymous functions.  Binding to the function object returned
by an inner function def is a more liberal strategy, allows for more
complexity.

This thread will document these suggestions and nuances.

Kirby
_______________________________________________
Edu-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/edu-sig