Re: on writing a while loop for rolling two dice

2021-09-19 Thread Hope Rouselle
dn  writes:

[...]

> Further, if you look at the OP's original solution, it only publishes
> the last pair, ie the match, without mention of the list of non-matches.
> Was it perhaps only a means of testing the solution?

It was a means of showing the student that indeed they obtained a match.
If the exercise asked for returning a single die or no die at all, they
would make mistakes and there'd be no sign of them being wrong.  For
instance, one trouble a lot of them went through was to start counting
from zero and so their number of rolls was off by one.  (I didn't see
that coming!)  The reason they fall for this is that they also test
little --- for some it never occurred a match on the first roll, so they
never saw the zero counter coming out.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-08 Thread Hope Rouselle
Joe Pfeiffer  writes:

> Hope Rouselle  writes:
>> Christian Gollwitzer  writes:
>>>
>>> I believe it is not commutativity, but associativity, that is
>>> violated. 
>>
>> Shall we take this seriously?  (I will disagree, but that doesn't mean I
>> am not grateful for your post.  Quite the contary.)  It in general
>> violates associativity too, but the example above couldn't be referring
>> to associativity because the second sum above could not be obtained from
>> associativity alone.  Commutativity is required, applied to five pairs
>> of numbers.  How can I go from
>>
>>   7.23 + 8.41 + 6.15 + 2.31 + 7.73 + 7.77
>>
>> to 
>>
>>   8.41 + 6.15 + 2.31 + 7.73 + 7.77 + 7.23?
>>
>> Perhaps only through various application of commutativity, namely the
>> ones below. (I omit the parentheses for less typing.  I suppose that
>> does not create much trouble.  There is no use of associativity below,
>> except for the intented omission of parentheses.)
>>
>>  7.23 + 8.41 + 6.15 + 2.31 + 7.73 + 7.77
>>= 8.41 + 7.23 + 6.15 + 2.31 + 7.73 + 7.77
>>= 8.41 + 6.15 + 7.23 + 2.31 + 7.73 + 7.77
>>= 8.41 + 6.15 + 2.31 + 7.23 + 7.73 + 7.77
>>= 8.41 + 6.15 + 2.31 + 7.73 + 7.23 + 7.77
>>= 8.41 + 6.15 + 2.31 + 7.73 + 7.77 + 7.23.
>
> But these transformations depend on both commutativity and
> associativity, precisely due to those omitted parentheses.  When you
> transform
>
> 7.23 + 8.41 + 6.15 + 2.31 + 7.73 + 7.77
>
> into
>
> 8.41 + 6.15 + 2.31 + 7.73 + 7.77 + 7.23.
>
> it isn't just assuming commutativity, it's also assuming associativity
> since it is changing from
>
> (7.23 + 8.41 + 6.15 + 2.31 + 7.73) + 7.77
>
> to
>
> (8.41 + 6.15 + 2.31 + 7.73 + 7.77) + 7.23.
> 
> If I use parentheses to modify the order of operations of the first line
> to match that of the last, I get
> 7.23 + (8.41 + 6.15 + 2.31 + 7.73 + 7.77)
>
> Now, I get 39.61 evaluating either of them.

I need to go slow.  If I have just two numbers, then I don't need to
talk about associativity: I can send 7.23 to the rightmost place with a
single application of commutativity.  In symbols,

  7.23 + 8.41 = 8.41 + 7.23.

But if I have three numbers and I want to send the leftmost to the
rightmost place, I need to apply associativity

7.23 + 8.41 + 6.15
  = (7.23 + 8.41) + 6.15  -- clarifying that I go left to right
  = 7.23 + (8.41 + 6.15)  -- associativity
  = (8.41 + 6.15) + 7.23  -- commutativity

I see it.  Cool.  Thanks.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on writing a while loop for rolling two dice

2021-09-07 Thread Hope Rouselle
Grant Edwards  writes:

> On 2021-09-06, Stefan Ram  wrote:
>> "Avi Gross"  writes:
>>> In languages like C/C++ there are people who make up macros like:
>>>#define INDEFINITELY_LOOP while (true)
>>>Or something like that and then allow the preprocessor to replace 
>>>INDEFINITELY_LOOP with valid C code.
>>
>>   Those usually are beginners.

[...]

>>   Such a use of macros is frowned upon by most C programmers,
>>   because it renders the code unreadable.
>
> I remember engineering manager I worked with about 35 years ago who
> used a set of C macros to try to make his code look as much like BASIC
> as possible:
>
>   #define IF if (
>   #define THEN ) {
>   #define ELSE } else {
>   #define ENDIF }
>   ...
>
> IIRC he copied them out of a magazine article.
>
> He then proceeded to try to implement a tree search algorithm (he
> didn't actually know that's what he was doing) using his new
> "language" without using recursion (which he had never heard of and
> couldn't grok) by keeping track of state using an array. It did not go
> well and made him a bit of a laughingstock. IIRC, he had first tried
> to write it in actual BASIC, but gave up on that before switching to C
> and his ridiculous macro set.

LOL!  (Had fun reading this.)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-06 Thread Hope Rouselle
Chris Angelico  writes:

> On Sun, Sep 5, 2021 at 1:04 PM Hope Rouselle  wrote:
>> The same question in other words --- what's a trivial way for the REPL
>> to show me such cycles occur?
>>
>> >>>>>> 7.23.as_integer_ratio()
>> >>> (2035064081618043, 281474976710656)
>>
>> Here's what I did on this case.  The REPL is telling me that
>>
>>   7.23 = 2035064081618043/281474976710656
>>
>> If that were true, then 7.23 * 281474976710656 would have to equal
>> 2035064081618043.  So I typed:
>>
>> >>> 7.23 * 281474976710656
>> 2035064081618043.0
>>
>> That agrees with the falsehood.  I'm getting no evidence of the problem.
>>
>> When take control of my life out of the hands of misleading computers, I
>> calculate the sum:
>>
>>844424930131968
>>  +5629499534213120
>> 197032483697459200
>> ==
>> 203506408161804288
>> =/= 203506408161804300
>>
>> How I can save the energy spent on manual verification?
>
> What you've stumbled upon here is actually a neat elegance of
> floating-point, and an often-forgotten fundamental of it: rounding
> occurs exactly the same regardless of the scale. The number 7.23 is
> represented with a certain mantissa, and multiplying it by some power
> of two doesn't change the mantissa, only the exponent. So the rounding
> happens exactly the same, and it comes out looking equal!

That's insightful.  Thanks!

> The easiest way, in Python, to probe this sort of thing is to use
> either fractions.Fraction or decimal.Decimal. I prefer Fraction, since
> a float is fundamentally a rational number, and you can easily see
> what's happening. You can construct a Fraction from a string, and
> it'll do what you would expect; or you can construct one from a float,
> and it'll show you what that float truly represents.
>
> It's often cleanest to print fractions out rather than just dumping
> them to the console, since the str() of a fraction looks like a
> fraction, but the repr() looks like a constructor call.
>
>>>> Fraction(0.25)
> Fraction(1, 4)
>>>> Fraction(0.1)
> Fraction(3602879701896397, 36028797018963968)
>
> If it looks like the number you put in, it was perfectly
> representable. If it looks like something of roughly that many digits,
> it's probably not the number you started with.

That's pretty, pretty nice.  It was really what I was looking for.

-- 
You're the best ``little lord of local nonsense'' I've ever met! :-D
(Lol.  The guy is kinda stressed out!  Plonk, plonk, plonk.  EOD.)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-06 Thread Hope Rouselle
"Peter J. Holzer"  writes:

> On 2021-09-05 03:38:55 +1200, Greg Ewing wrote:
>> If 7.23 were exactly representable, you would have got
>> 723/1000.
>> 
>> Contrast this with something that *is* exactly representable:
>> 
>> >>> 7.875.as_integer_ratio()
>> (63, 8)
>> 
>> and observe that 7875/1000 == 63/8:
>> 
>> >>> from fractions import Fraction
>> >>> Fraction(7875,1000)
>> Fraction(63, 8)
>> 
>> In general, to find out whether a decimal number is exactly
>> representable in binary, represent it as a ratio of integers
>> where the denominator is a power of 10, reduce that to lowest
>> terms,
>
> ... and check if the denominator is a power of two. If it isn't (e.g.
> 1000 == 2**3 * 5**3) then the number is not exactly representable as a
> binary floating point number.
>
> More generally, if the prime factorization of the denominator only
> contains prime factors which are also prime factors of your base, then
> the number can be exactle represented (unless either the denominator or
> the enumerator get too big). So, for base 10 (2*5), all numbers which
> have only powers of 2 and 5 in the denominator (e.g 1/10 == 1/(2*5),
> 1/8192 == 1/2**13, 1/1024000 == 1/(2**13 * 5**3)) can represented
> exactly, but those with other prime factors (e.g. 1/3, 1/7,
> 1/24576 == 1/(2**13 * 3), 1/1024001 == 1/(11 * 127 * 733)) cannot.
> Similarly, for base 12 (2*2*3) numbers with 2 and 3 in the denominator
> can be represented and for base 60 (2*2*3*5), numbers with 2, 3 and 5.

Very grateful to these paragraphs.  They destroy all the mystery.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on writing a while loop for rolling two dice

2021-09-04 Thread Hope Rouselle
"Michael F. Stemper"  writes:

> On 04/09/2021 08.53, Hope Rouselle wrote:
>> Chris Angelico  writes:
>
>>> And at this point, it's looking pretty much identical to the for loop
>>> version. Ultimately, they're all the same and you can pick and choose
>>> elements from each of them.
>> I see.  That's why C must have added the do-while, but yeah --- it's
>> not
>> really worth it.
>
> Kernighan and Ritchie agree(d) with you. Per _The C Programming
> Language__:
>   Experience shows that do-while is much less used that while
>   and for.
>
> (Second edition, Section 3.6, page 63)

Special thanks for the reference!  Here's what they say on the first
edition.

  ``As might be expected, do-while is much less used than while and for,
  accounting for perhaps five percent of all loops.''  (First edition,
  section 3.6, page 59.)  

They looked into a sample and decided to remove the statistic. :-)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Problem with python

2021-09-04 Thread Hope Rouselle
Igor Korot  writes:

> Hi,
> Will this syntax work in python 2?

If you say

  print(something)

it works in both.  So, stick to this syntax.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-04 Thread Hope Rouselle
Christian Gollwitzer  writes:

> Am 04.09.21 um 14:48 schrieb Hope Rouselle:
>> Christian Gollwitzer  writes:
>> 
>>> Am 02.09.21 um 15:51 schrieb Hope Rouselle:
>>>> Just sharing a case of floating-point numbers.  Nothing needed to be
>>>> solved or to be figured out.  Just bringing up conversation.
>>>> (*) An introduction to me
>>>> I don't understand floating-point numbers from the inside out, but I
>>>> do
>>>> know how to work with base 2 and scientific notation.  So the idea of
>>>> expressing a number as
>>>> mantissa * base^{power}
>>>> is not foreign to me. (If that helps you to perhaps instruct me on
>>>> what's going on here.)
>>>> (*) A presentation of the behavior
>>>>
>>>>>>> import sys
>>>>>>> sys.version
>>>> '3.8.10 (tags/v3.8.10:3d8993a, May 3 2021, 11:48:03) [MSC v.1928 64
>>>> bit (AMD64)]'
>>>>
>>>>>>> ls = [7.23, 8.41, 6.15, 2.31, 7.73, 7.77]
>>>>>>> sum(ls)
>>>> 39.594
>>>>
>>>>>>> ls = [8.41, 6.15, 2.31, 7.73, 7.77, 7.23]
>>>>>>> sum(ls)
>>>> 39.61
>>>> All I did was to take the first number, 7.23, and move it to the
>>>> last
>>>> position in the list.  (So we have a violation of the commutativity of
>>>> addition.)
>>>
>>> I believe it is not commutativity, but associativity, that is
>>> violated.
>> Shall we take this seriously?  (I will disagree, but that doesn't
>> mean I
>> am not grateful for your post.  Quite the contary.)  It in general
>> violates associativity too, but the example above couldn't be referring
>> to associativity because the second sum above could not be obtained from
>> associativity alone.  Commutativity is required, applied to five pairs
>> of numbers.  How can I go from
>>7.23 + 8.41 + 6.15 + 2.31 + 7.73 + 7.77
>> to
>>8.41 + 6.15 + 2.31 + 7.73 + 7.77 + 7.23?
>> Perhaps only through various application of commutativity, namely
>> the
>> ones below. (I omit the parentheses for less typing.  I suppose that
>> does not create much trouble.  There is no use of associativity below,
>> except for the intented omission of parentheses.)
>
> With the parens it will become more obvious.
>
>>   7.23 + 8.41 + 6.15 + 2.31 + 7.73 + 7.77
>> = 8.41 + 7.23 + 6.15 + 2.31 + 7.73 + 7.77
>
> The sum is evaluated as
>
>   (((7.23 + 8.41) + 6.15 + ...)
>
> For the first shift, you are correct that commutativity will result in
>
>   (((8.41 + 7.23) + 6.15 + ...)
>
> But you can't go in one step to
>
>   (((8.41 + 6.15) + 7.23 + ...)
>
> with  the commutativity law alone. Instead, a sequence of
> associativity and commutativity is required to move the 7.23 out of
> the first pair of parentheses.
>
> And what I was trying to say, the commutative steps *are* equal in
> floating point arithmetics, whereas the associative steps are not.

Oh, I see it.  Very good point!  Lesson learned.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-04 Thread Hope Rouselle
Julio Di Egidio  writes:

[...]

>> I, too, lost my patience there. :-)
>
> As if I didn't know who's trolling...

I never trolled you.  When we had our conversations in sci.logic, I was
Boris Dorestand --- you would remember if you have very good memory.  We
talked for just a few days, I guess.  The people trolling you there were
not me.  So, though that's no proof, *I* know you didn't know. :-)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-04 Thread Hope Rouselle
Hope Rouselle  writes:

> Greg Ewing  writes:
>
>> On 5/09/21 2:42 am, Hope Rouselle wrote:
>>> Here's what I did on this case.  The REPL is telling me that
>>>7.23 = 2035064081618043/281474976710656
>>
>> If 7.23 were exactly representable, you would have got
>> 723/1000.
>>
>> Contrast this with something that *is* exactly representable:
>>
>>>>> 7.875.as_integer_ratio()
>> (63, 8)
>>
>> and observe that 7875/1000 == 63/8:
>>
>>>>> from fractions import Fraction
>>>>> Fraction(7875,1000)
>> Fraction(63, 8)
>>
>> In general, to find out whether a decimal number is exactly
>> representable in binary, represent it as a ratio of integers
>> where the denominator is a power of 10, reduce that to lowest
>> terms, and compare with the result of as_integer_ratio().
>
> That makes perfect sense and answers my question.  I appreciate it.

Here's my homework in high-precision.  Thoughts?  Thank you!

--8<---cut here---start->8---
def is_representable(s):
  return in_lowest_terms(rat_power_of_10(s)) == float(s).as_integer_ratio()

# >>> is_representable("1.5")
# True
# 
# >>> is_representable("0.1")
# False

def rat_power_of_10(s):
  """I assume s is a numeric string in the format ."""
  if "." not in s:
return int(s), 1
  integral, fractional = s.split(".")
  return int(integral + fractional), 10**(len(fractional))

# >>> rat_power_of_10("72.100")
# (72100, 1000)

def in_lowest_terms(rat):
  from math import gcd
  n, d = rat
  return n//gcd(n, d),  d//gcd(n, d)

# >>> in_lowest_terms( (72100, 1000) )
# (721, 10)
--8<---cut here---end--->8---
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-04 Thread Hope Rouselle
Richard Damon  writes:

> On 9/4/21 9:40 AM, Hope Rouselle wrote:
>> Chris Angelico  writes:
>> 
>>> On Fri, Sep 3, 2021 at 4:58 AM Hope Rouselle  wrote:
>>>>
>>>> Hope Rouselle  writes:
>>>>
>>>>> Just sharing a case of floating-point numbers.  Nothing needed to be
>>>>> solved or to be figured out.  Just bringing up conversation.
>>>>>
>>>>> (*) An introduction to me
>>>>>
>>>>> I don't understand floating-point numbers from the inside out, but I do
>>>>> know how to work with base 2 and scientific notation.  So the idea of
>>>>> expressing a number as
>>>>>
>>>>>   mantissa * base^{power}
>>>>>
>>>>> is not foreign to me. (If that helps you to perhaps instruct me on
>>>>> what's going on here.)
>>>>>
>>>>> (*) A presentation of the behavior
>>>>>
>>>>>>>> import sys
>>>>>>>> sys.version
>>>>> '3.8.10 (tags/v3.8.10:3d8993a, May 3 2021, 11:48:03) [MSC v.1928 64
>>>>> bit (AMD64)]'
>>>>>
>>>>>>>> ls = [7.23, 8.41, 6.15, 2.31, 7.73, 7.77]
>>>>>>>> sum(ls)
>>>>> 39.594
>>>>>
>>>>>>>> ls = [8.41, 6.15, 2.31, 7.73, 7.77, 7.23]
>>>>>>>> sum(ls)
>>>>> 39.61
>>>>>
>>>>> All I did was to take the first number, 7.23, and move it to the last
>>>>> position in the list.  (So we have a violation of the commutativity of
>>>>> addition.)
>>>>
>>>> Suppose these numbers are prices in dollar, never going beyond cents.
>>>> Would it be safe to multiply each one of them by 100 and therefore work
>>>> with cents only?  For instance
>>>
>>> Yes and no. It absolutely *is* safe to always work with cents, but to
>>> do that, you have to be consistent: ALWAYS work with cents, never with
>>> floating point dollars.
>>>
>>> (Or whatever other unit you choose to use. Most currencies have a
>>> smallest-normally-used-unit, with other currency units (where present)
>>> being whole number multiples of that minimal unit. Only in forex do
>>> you need to concern yourself with fractional cents or fractional yen.)
>>>
>>> But multiplying a set of floats by 100 won't necessarily solve your
>>> problem; you may have already fallen victim to the flaw of assuming
>>> that the numbers are represented accurately.
>> 
>> Hang on a second.  I see it's always safe to work with cents, but I'm
>> only confident to say that when one gives me cents to start with.  In
>> other words, if one gives me integers from the start.  (Because then, of
>> course, I don't even have floats to worry about.)  If I'm given 1.17,
>> say, I am not confident that I could turn this number into 117 by
>> multiplying it by 100.  And that was the question.  Can I always
>> multiply such IEEE 754 dollar amounts by 100?
>> 
>> Considering your last paragraph above, I should say: if one gives me an
>> accurate floating-point representation, can I assume a multiplication of
>> it by 100 remains accurately representable in IEEE 754?
>> 
>
> Multiplication by 100 might not be accurate if the number you are
> starting with is close to the limit of precision, because 100 is
> 1.1001 x 64 so multiplying by 100 adds about 5 more 'bits' to the
> representation of the number. In your case, the numbers are well below
> that point.

Alright.  That's clear now.  Thanks so much!

>>>> --8<---cut here---start->8---
>>>>>>> ls = [7.23, 8.41, 6.15, 2.31, 7.73, 7.77]
>>>>>>> sum(map(lambda x: int(x*100), ls)) / 100
>>>> 39.6
>>>>
>>>>>>> ls = [8.41, 6.15, 2.31, 7.73, 7.77, 7.23]
>>>>>>> sum(map(lambda x: int(x*100), ls)) / 100
>>>> 39.6
>>>> --8<---cut here---end--->8---
>>>>
>>>> Or multiplication by 100 isn't quite ``safe'' to do with floating-point
>>>> numbers either?  (It worked in this case.)
>>>
>>> You're multiplying and then truncating, which risks a round-down
>>> error. Try adding a half onto them first:
>>>
>>> int(x * 100 + 0.5)
>>>
>>> But that's still not a perfect guarantee. Far safer would be t

Re: on floating-point numbers

2021-09-04 Thread Hope Rouselle
Greg Ewing  writes:

> On 5/09/21 2:42 am, Hope Rouselle wrote:
>> Here's what I did on this case.  The REPL is telling me that
>>7.23 = 2035064081618043/281474976710656
>
> If 7.23 were exactly representable, you would have got
> 723/1000.
>
> Contrast this with something that *is* exactly representable:
>
>>>> 7.875.as_integer_ratio()
> (63, 8)
>
> and observe that 7875/1000 == 63/8:
>
>>>> from fractions import Fraction
>>>> Fraction(7875,1000)
> Fraction(63, 8)
>
> In general, to find out whether a decimal number is exactly
> representable in binary, represent it as a ratio of integers
> where the denominator is a power of 10, reduce that to lowest
> terms, and compare with the result of as_integer_ratio().

That makes perfect sense and answers my question.  I appreciate it.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-04 Thread Hope Rouselle
Julio Di Egidio  writes:

> On Thursday, 2 September 2021 at 15:52:02 UTC+2, Hope Rouselle wrote:
>
>> I don't understand floating-point numbers from the inside out, but I do 
>> know how to work with base 2 and scientific notation. So the idea of 
>> expressing a number as 
>> 
>> mantissa * base^{power} 
>
> That's the basic idea, but the actual (ISO) floating-point *encoding*
> is more complicated than that.
>
>> is not foreign to me. (If that helps you to perhaps instruct me on 
>> what's going on here.) 
>
> This is the "classic":
> DAVID GOLDBER
> What Every Computer Scientist Should Know About Floating-Point Arithmetic
> <http://perso.ens-lyon.fr/jean-michel.muller/goldberg.pdf>
>
> Here is some more introductory stuff:
> <https://en.wikipedia.org/wiki/Floating-point_arithmetic>
> <https://www.phys.uconn.edu/~rozman/Courses/P2200_15F/downloads/floating-point-guide-2015-10-15.pdf>

Rozman's was pretty concise and nice.  Thank you.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on writing a while loop for rolling two dice

2021-09-04 Thread Hope Rouselle
"Peter J. Holzer"  writes:

> On 2021-09-02 11:28:21 -0300, Hope Rouselle wrote:
>> dn  writes:
>> > On 29/08/2021 08.46, Hope Rouselle wrote:
>> >> Here's my solution:
>> >> 
>> >> --8<---cut here---start->8---
>> >> def how_many_times():
>> >>   x, y = 0, 1
>> >>   c = 0
>> >>   while x != y:
>> >> c = c + 1
>> >> x, y = roll()
>> >>   return c, (x, y)
>> >
>> >> 
>> >> Why am I unhappy?  I'm wish I could confine x, y to the while loop.
>> >> The introduction of ``x, y = 0, 1'' must feel like a trick to a
>> >> novice.  How would you write this?
> [...]
>> But perhaps we may agree that while rolling dice until a certain
>> success, we want to roll them while something happens or doesn't happen.
>> One of the two.  So while-True is a bit of a jump.  Therefore, in this
>> case, the easier and more natural option is to say while-x-not-equal-y.
>> 
>> But this approach seems to force me into initializing x, y with
>> different values.
>
> You can get around this by using NaN:
>
> def how_many_times():
>   c, x, y = 0, math.nan, math.nan
>   while x != y:
> c = c + 1
> x, y = roll()
>   return c, x, y
>
> Not sure if this is an improvement. Now you have to explain to your
> students why math.nan != math.nan.
>
> When I need a guaranteed unique value I often just use object():
>
> def how_many_times():
>   c, x, y = 0, object(), object()
>   while x != y:
> c = c + 1
> x, y = roll()
>   return c, x, y
>
> Of course now you are back to two different values, but they look the
> same. Which may be better or worse for your students. Plus x and y are
> now bound to objects of different types during their lifetime, which may
> be a bit dicey.

Lol.  I would argue that it's quite appropriate to the event (``of
rolling dice'' --- clarity-obsession). :D Pretty nice alternatives.
Thank you so much.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-04 Thread Hope Rouselle
Julio Di Egidio  writes:

> On Thursday, 2 September 2021 at 16:51:24 UTC+2, Christian Gollwitzer wrote:
>> Am 02.09.21 um 16:49 schrieb Julio Di Egidio:
>> > On Thursday, 2 September 2021 at 16:41:38 UTC+2, Peter Pearson wrote: 
>> >> On Thu, 02 Sep 2021 10:51:03 -0300, Hope Rouselle wrote: 
>> > 
>> >>> 39.61 
>> >> 
>> >> Welcome to the exciting world of roundoff error: 
>> > 
>> > Welcome to the exiting world of Usenet. 
>> > 
>> > *Plonk*
>> 
>> Pretty harsh, isn't it? He gave a concise example of the same inaccuracy 
>> right afterwards. 
>
> And I thought you were not seeing my posts...
>
> Given that I have already given a full explanation, you guys, that you
> realise it or not, are simply adding noise for the usual pub-level
> discussion I must most charitably guess.
>
> Anyway, just my opinion.  (EOD.)

Which is certainly appreciated --- as a rule.  Pub-level noise is pretty
much unavoidable in investigation, education.  Being wrong is, too,
unavoidable in investigation, education.  There is a point we eventually
publish at the most respected journals, but that's a whole other
interval of the time-line.  IOW, chill out! :-D (Give us a C-k and meet
us up in the next thread.  Oh, my, you're not a Gnus user: you are a
G2/1.0 user.  That's pretty scary.)

By the way, how's sci.logic going?  I, too, lost my patience there. :-)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on writing a while loop for rolling two dice

2021-09-04 Thread Hope Rouselle
Chris Angelico  writes:

> On Fri, Sep 3, 2021 at 4:33 AM Hope Rouselle  wrote:
>> Yeah.  Here's a little context.  I came across this by processing a list
>> of exercises.  (I'm teaching a course --- you know that by now, I
>> guess.)  So the first thing I observed was the equal volume of work
>> dedicated to while loops and for loops --- so I decided to compared
>> which appeared more often in a certain sample of well-written Python
>> code.  It turns out the for loop was much more frequent.  Students have
>> been reporting too much work in too little time, so I decided to reduce
>> the number of exercises involving while loops.  When I began to look at
>> the exercises, to see which ones I'd exclude, I decided to exclude them
>> all --- lol! --- except for one.  The one that remained was this one
>> about rolling dice until a satisfying result would appear.  (All other
>> ones were totally more naturally written with a for loop.)
>>
>> So if I were to also write this with a for-loop, it'd defeat the purpose
>> of the course's moment.  Besides, I don't think a for-loop would improve
>> the readability here.
>
> It's on the cusp. When you ask someone to express the concept of "do
> this until this happens", obviously that's a while loop; but as soon
> as you introduce the iteration counter, it becomes less obvious, since
> "iterate over counting numbers until this happens" is a quite viable
> way to express this. However, if the students don't know
> itertools.count(), they'll most likely put in an arbitrary limit (like
> "for c in range(1)"), which you can call them out for.
>
>> But I thought your protest against the while-True was very well put:
>> while-True is not too readable for a novice.  Surely what's readable or
>> more-natural /to someone/ is, well, subjective (yes, by definition).
>> But perhaps we may agree that while rolling dice until a certain
>> success, we want to roll them while something happens or doesn't happen.
>> One of the two.  So while-True is a bit of a jump.  Therefore, in this
>> case, the easier and more natural option is to say while-x-not-equal-y.
>
> That may be the case, but in Python, I almost never write "while
> True". Consider the two while loops in this function:
>
> https://github.com/Rosuav/shed/blob/master/autohost_manager.py#L92
>
> Thanks to Python's flexibility and efficient compilation, these loops
> are as descriptive as those with actual conditions, while still
> behaving exactly like "while True". (The inner loop, "more pages",
> looks superficially like it should be a for loop - "for page in
> pages:" - but the data is coming from successive API calls, so it
> can't know.)

That's pretty nice.  I did suggest the same to my students, showing your
code to them, actually.  The course explicitly avoided talking about
regular values being considered True, but now I couldn't keep the truth
from them.

>> I don't see it.  You seem to have found what we seem to agree that it
>> would be the more natural way to write the strategy.  But I can't see
>> it.  It certainly isn't
>>
>> --8<---cut here---start->8---
>> def how_many_times_1():
>>   c, x, y = 0, None, None
>>   while x != y:
>> c = c + 1
>> x, y = roll()
>>   return c, x, y
>> --8<---cut here---end--->8---
>>
>> nor
>>
>> --8<---cut here---start->8---
>> def how_many_times_2():
>>   c, x, y = 0, None, None
>>   while x == y:
>> c = c + 1
>> x, y = dados()
>>   return c, x, y
>> --8<---cut here---end--->8---
>>
>> What do you have in mind?  I couldn't see it.
>
> You're overlaying two loops here. One is iterating "c" up from zero,
> the other is calling a function and testing its results. It's up to
> you which of these should be considered the more important, and which
> is a bit of extra work added onto it. With the counter as primary, you
> get something like this:
>
> for c in itertools.count():
> x, y = roll()
> if x == y: return c, x, y
>
> With the roll comparison as primary, you get this:
>
> c, x, y = 0, 0, 1
> while x != y:
> x, y = roll()
> c += 1
> return c, x, y
>
> Reworking the second into a do-while style (Python doesn't have that,
> so we have to write it manually):
>
> c = 0
> while "x and y differ":
> x, y = roll()
> c += 1
> if x == y: break
> return c, x, y
>
> And at this point, it's looking pretty much identical to the for loop
> version. Ultimately, they're all the same and you can pick and choose
> elements from each of them.

I see.  That's why C must have added the do-while, but yeah --- it's not
really worth it.  (An educational investigation.  Thank you.)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-04 Thread Hope Rouselle
Christian Gollwitzer  writes:

> Am 02.09.21 um 15:51 schrieb Hope Rouselle:
>> Just sharing a case of floating-point numbers.  Nothing needed to be
>> solved or to be figured out.  Just bringing up conversation.
>> (*) An introduction to me
>> I don't understand floating-point numbers from the inside out, but I
>> do
>> know how to work with base 2 and scientific notation.  So the idea of
>> expressing a number as
>>mantissa * base^{power}
>> is not foreign to me. (If that helps you to perhaps instruct me on
>> what's going on here.)
>> (*) A presentation of the behavior
>> 
>>>>> import sys
>>>>> sys.version
>> '3.8.10 (tags/v3.8.10:3d8993a, May 3 2021, 11:48:03) [MSC v.1928 64
>> bit (AMD64)]'
>> 
>>>>> ls = [7.23, 8.41, 6.15, 2.31, 7.73, 7.77]
>>>>> sum(ls)
>> 39.594
>> 
>>>>> ls = [8.41, 6.15, 2.31, 7.73, 7.77, 7.23]
>>>>> sum(ls)
>> 39.61
>> All I did was to take the first number, 7.23, and move it to the
>> last
>> position in the list.  (So we have a violation of the commutativity of
>> addition.)
>
> I believe it is not commutativity, but associativity, that is
> violated. 

Shall we take this seriously?  (I will disagree, but that doesn't mean I
am not grateful for your post.  Quite the contary.)  It in general
violates associativity too, but the example above couldn't be referring
to associativity because the second sum above could not be obtained from
associativity alone.  Commutativity is required, applied to five pairs
of numbers.  How can I go from

  7.23 + 8.41 + 6.15 + 2.31 + 7.73 + 7.77

to 

  8.41 + 6.15 + 2.31 + 7.73 + 7.77 + 7.23?

Perhaps only through various application of commutativity, namely the
ones below. (I omit the parentheses for less typing.  I suppose that
does not create much trouble.  There is no use of associativity below,
except for the intented omission of parentheses.)

 7.23 + 8.41 + 6.15 + 2.31 + 7.73 + 7.77
   = 8.41 + 7.23 + 6.15 + 2.31 + 7.73 + 7.77
   = 8.41 + 6.15 + 7.23 + 2.31 + 7.73 + 7.77
   = 8.41 + 6.15 + 2.31 + 7.23 + 7.73 + 7.77
   = 8.41 + 6.15 + 2.31 + 7.73 + 7.23 + 7.77
   = 8.41 + 6.15 + 2.31 + 7.73 + 7.77 + 7.23.
   
> Even for floating point, a+b=b+a except for maybe some extreme cases
> like denormliazed numbers etc.
>
> But in general (a+b)+c != a+ (b+c)
>
> Consider decimal floating point with 2 digits.
> a=1
> b=c=0.04
>
> Then you get LHS;
>  (1 + 0.04) + 0.04 = 1 + 0.04 = 1
>
> RHS:
>
> 1 + (0.04 + 0.04) = 1 + 0.08 = 1.1
>
> Your sum is evaluated like (((a + b) + c) + ) and hence, if you
> permute the numbers, it can be unequal. If you need better accuracy, 
> there is the Kahan summation algorithm and other alternatives:
> https://en.wikipedia.org/wiki/Kahan_summation_algorithm

Thanks!
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-04 Thread Hope Rouselle
Chris Angelico  writes:

> On Fri, Sep 3, 2021 at 4:58 AM Hope Rouselle  wrote:
>>
>> Hope Rouselle  writes:
>>
>> > Just sharing a case of floating-point numbers.  Nothing needed to be
>> > solved or to be figured out.  Just bringing up conversation.
>> >
>> > (*) An introduction to me
>> >
>> > I don't understand floating-point numbers from the inside out, but I do
>> > know how to work with base 2 and scientific notation.  So the idea of
>> > expressing a number as
>> >
>> >   mantissa * base^{power}
>> >
>> > is not foreign to me. (If that helps you to perhaps instruct me on
>> > what's going on here.)
>> >
>> > (*) A presentation of the behavior
>> >
>> >>>> import sys
>> >>>> sys.version
>> > '3.8.10 (tags/v3.8.10:3d8993a, May 3 2021, 11:48:03) [MSC v.1928 64
>> > bit (AMD64)]'
>> >
>> >>>> ls = [7.23, 8.41, 6.15, 2.31, 7.73, 7.77]
>> >>>> sum(ls)
>> > 39.594
>> >
>> >>>> ls = [8.41, 6.15, 2.31, 7.73, 7.77, 7.23]
>> >>>> sum(ls)
>> > 39.61
>> >
>> > All I did was to take the first number, 7.23, and move it to the last
>> > position in the list.  (So we have a violation of the commutativity of
>> > addition.)
>>
>> Suppose these numbers are prices in dollar, never going beyond cents.
>> Would it be safe to multiply each one of them by 100 and therefore work
>> with cents only?  For instance
>
> Yes and no. It absolutely *is* safe to always work with cents, but to
> do that, you have to be consistent: ALWAYS work with cents, never with
> floating point dollars.
>
> (Or whatever other unit you choose to use. Most currencies have a
> smallest-normally-used-unit, with other currency units (where present)
> being whole number multiples of that minimal unit. Only in forex do
> you need to concern yourself with fractional cents or fractional yen.)
>
> But multiplying a set of floats by 100 won't necessarily solve your
> problem; you may have already fallen victim to the flaw of assuming
> that the numbers are represented accurately.

Hang on a second.  I see it's always safe to work with cents, but I'm
only confident to say that when one gives me cents to start with.  In
other words, if one gives me integers from the start.  (Because then, of
course, I don't even have floats to worry about.)  If I'm given 1.17,
say, I am not confident that I could turn this number into 117 by
multiplying it by 100.  And that was the question.  Can I always
multiply such IEEE 754 dollar amounts by 100?

Considering your last paragraph above, I should say: if one gives me an
accurate floating-point representation, can I assume a multiplication of
it by 100 remains accurately representable in IEEE 754?

>> --8<---cut here---start->8---
>> >>> ls = [7.23, 8.41, 6.15, 2.31, 7.73, 7.77]
>> >>> sum(map(lambda x: int(x*100), ls)) / 100
>> 39.6
>>
>> >>> ls = [8.41, 6.15, 2.31, 7.73, 7.77, 7.23]
>> >>> sum(map(lambda x: int(x*100), ls)) / 100
>> 39.6
>> --8<---cut here---end--->8---
>>
>> Or multiplication by 100 isn't quite ``safe'' to do with floating-point
>> numbers either?  (It worked in this case.)
>
> You're multiplying and then truncating, which risks a round-down
> error. Try adding a half onto them first:
>
> int(x * 100 + 0.5)
>
> But that's still not a perfect guarantee. Far safer would be to
> consider monetary values to be a different type of value, not just a
> raw number. For instance, the value $7.23 could be stored internally
> as the integer 723, but you also know that it's a value in USD, not a
> simple scalar. It makes perfect sense to add USD+USD, it makes perfect
> sense to multiply USD*scalar, but it doesn't make sense to multiply
> USD*USD.

Because of the units?  That would be USD squared?  (Nice analysis.)

>> I suppose that if I multiply it by a power of two, that would be an
>> operation that I can be sure will not bring about any precision loss
>> with floating-point numbers.  Do you agree?
>
> Assuming you're nowhere near 2**53, yes, that would be safe. But so
> would multiplying by a power of five. The problem isn't precision loss
> from the multiplication - the problem is that your input numbers
> aren't what you think they are. That number 7.23, for instance, is
> really

Hm, I think I see what you're saying.  You're saying multiplication and
division in IEEE 754 is perfectly safe --- so

Re: on the popularity of loops while and for

2021-09-04 Thread Hope Rouselle
"Peter J. Holzer"  writes:

> On 2021-08-29 10:04:47 +0100, Barry wrote:
>> > I'd like get a statistic of how often each loop is used in practice.  
>> > 
>> > I was trying to take a look at the Python's standard libraries --- those
>> > included in a standard installation of Python 3.9.6, say --- to see
>> > which loops are more often used among while and for loops.  Of course,
>> > since English use the preposition ``for'' a lot, that makes my life
>> > harder.  Removing comments is easy, but removing strings is harder.  So
>> > I don't know yet what I'll do.
>> > 
>> > Have you guys ever measured something like that in a casual or serious
>> > way?  I'd love to know.  Thank you!
>> 
>> I am interesting in why you think that choice of while vs. for is
>> about popularity?
>> 
>> Surely the choice is made in most cases by the algorithm?
>
> The context here is an introductory course for Python. So there is not
> "the algorithm", there are all the algorithms that a novice is likely
> to encounter.
>
> For me it makes absolute sense to base the contents of the course on
> popularity. Constructs which a novice programmer is very likely to use
> or encounter in other people's code should be covered more thoroughly
> than constructs that will be used only rarely. Some are so rare that
> they can be safely omitted. The while loop is certainly not in that
> category, but it probably makes sense to spend less time on it than on
> the for loop.

``A man after my own heart.''
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-04 Thread Hope Rouselle
Chris Angelico  writes:

> On Fri, Sep 3, 2021 at 4:29 AM Hope Rouselle  wrote:
>>
>> Just sharing a case of floating-point numbers.  Nothing needed to be
>> solved or to be figured out.  Just bringing up conversation.
>>
>> (*) An introduction to me
>>
>> I don't understand floating-point numbers from the inside out, but I do
>> know how to work with base 2 and scientific notation.  So the idea of
>> expressing a number as
>>
>>   mantissa * base^{power}
>>
>> is not foreign to me. (If that helps you to perhaps instruct me on
>> what's going on here.)
>>
>> (*) A presentation of the behavior
>>
>> >>> import sys
>> >>> sys.version
>> '3.8.10 (tags/v3.8.10:3d8993a, May 3 2021, 11:48:03) [MSC v.1928 64
>> bit (AMD64)]'
>>
>> >>> ls = [7.23, 8.41, 6.15, 2.31, 7.73, 7.77]
>> >>> sum(ls)
>> 39.594
>>
>> >>> ls = [8.41, 6.15, 2.31, 7.73, 7.77, 7.23]
>> >>> sum(ls)
>> 39.61
>>
>> All I did was to take the first number, 7.23, and move it to the last
>> position in the list.  (So we have a violation of the commutativity of
>> addition.)
>
> It's not about the commutativity of any particular pair of operands -
> that's always guaranteed. 

Shall we take this seriously?  It has to be about the commutativity of
at least one particular pair because it is involved with the
commutavitity of a set of pairs.  If various pairs are involved, then at
least one is involved.  IOW, it is about the commutativity of some pair
of operands and so it could not be the case that it's not about the
commutativity of any.  (Lol.  I hope that's not too insubordinate.  I
already protested against a claim for associativity in this thread and
now I'm going for the king of the hill, for whom I have always been so
grateful!)

> What you're seeing here is the results of intermediate rounding. Try
> this:

Indeed.  Your post here is really great, as usual.  It offers a nice
tool for investigation.  Thank you so much.

>>>> def sum(stuff):
> ... total = 0
> ... for thing in stuff:
> ... total += thing
> ... print(thing, "-->", total)
> ... return total
> ...
>>>> ls = [7.23, 8.41, 6.15, 2.31, 7.73, 7.77]
>>>> sum(ls)
> 7.23 --> 7.23
> 8.41 --> 15.64
> 6.15 --> 21.79
> 2.31 --> 24.098

Alright.  Thanks so much for this example.  Here's a new puzzle for me.
The REPL makes me think that both 21.79 and 2.31 *are* representable
exactly in Python's floating-point datatype because I see:

>>> 2.31
2.31
>>> 21.79
21.79

When I add them, the result obtained makes me think that the sum is
*not* representable exactly in Python's floating-point number:

>>> 21.79 + 2.31
24.098

However, when I type 24.10 explicitly, the REPL makes me think that
24.10 *is* representable exactly:

>>> 24.10
24.1

I suppose I cannot trust the appearance of the representation?  What's
really going on there?  (Perhaps the trouble appears while Python is
computing the sum of the numbers 21.79 and 2.31?)  Thanks so much!
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-09-04 Thread Hope Rouselle
Dennis Lee Bieber  writes:

> On Sun, 22 Aug 2021 16:28:12 -0300, Hope Rouselle 
> declaimed the following:
>
>
>>That's wild. :-) Was this created by Brian Kernighan?  It's hard to
>>believe.  Oh, I think he wrote AMPL, wasn't it?  A Mathematical
>>Programming Language, or something like that.
>
>   Kenneth Iverson, early 1960s for release, though he started in the late
> 50s (so a decade before Kernighan). I believe it started life as a notation
> for publishing reports, not as an actual implemented language.
>
> <https://en.wikipedia.org/wiki/APL_(programming_language)#Development_into_a_computer_programming_language>
> {Hmmm, supposed to have influenced Matlab, S, and Wolfram/Mathematica}
>
>   One of the quirks is that one reads APL from right to left... cf:
> <https://en.wikipedia.org/wiki/APL_(programming_language)#Pick_6_lottery_numbers>
>
>   You do not want to look down at the one-liner for Conway's Game of
> Life.

I really do not.  But I did.  That's really wild.  Dramatic.  Speechless.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on floating-point numbers

2021-09-02 Thread Hope Rouselle
Hope Rouselle  writes:

> Just sharing a case of floating-point numbers.  Nothing needed to be
> solved or to be figured out.  Just bringing up conversation.
>
> (*) An introduction to me
>
> I don't understand floating-point numbers from the inside out, but I do
> know how to work with base 2 and scientific notation.  So the idea of
> expressing a number as 
>
>   mantissa * base^{power}
>
> is not foreign to me. (If that helps you to perhaps instruct me on
> what's going on here.)
>
> (*) A presentation of the behavior
>
>>>> import sys
>>>> sys.version
> '3.8.10 (tags/v3.8.10:3d8993a, May 3 2021, 11:48:03) [MSC v.1928 64
> bit (AMD64)]'
>
>>>> ls = [7.23, 8.41, 6.15, 2.31, 7.73, 7.77]
>>>> sum(ls)
> 39.594
>
>>>> ls = [8.41, 6.15, 2.31, 7.73, 7.77, 7.23]
>>>> sum(ls)
> 39.61
>
> All I did was to take the first number, 7.23, and move it to the last
> position in the list.  (So we have a violation of the commutativity of
> addition.)

Suppose these numbers are prices in dollar, never going beyond cents.
Would it be safe to multiply each one of them by 100 and therefore work
with cents only?  For instance

--8<---cut here---start->8---
>>> ls = [7.23, 8.41, 6.15, 2.31, 7.73, 7.77]
>>> sum(map(lambda x: int(x*100), ls)) / 100
39.6

>>> ls = [8.41, 6.15, 2.31, 7.73, 7.77, 7.23]
>>> sum(map(lambda x: int(x*100), ls)) / 100
39.6
--8<---cut here---end--->8---

Or multiplication by 100 isn't quite ``safe'' to do with floating-point
numbers either?  (It worked in this case.)

I suppose that if I multiply it by a power of two, that would be an
operation that I can be sure will not bring about any precision loss
with floating-point numbers.  Do you agree?
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on writing a while loop for rolling two dice

2021-09-02 Thread Hope Rouselle
Chris Angelico  writes:

> On Mon, Aug 30, 2021 at 11:13 PM David Raymond  
> wrote:
>>
>> > def how_many_times():
>> >   x, y = 0, 1
>> >   c = 0
>> >   while x != y:
>> > c = c + 1
>> > x, y = roll()
>> >   return c, (x, y)
>>
>> Since I haven't seen it used in answers yet, here's another option using our 
>> new walrus operator
>>
>> def how_many_times():
>> roll_count = 1
>> while (rolls := roll())[0] != rolls[1]:
>> roll_count += 1
>> return (roll_count, rolls)
>>
>
> Since we're creating solutions that use features in completely
> unnecessary ways, here's a version that uses collections.Counter:
>
> def how_many_times():
> return next((count, rolls) for count, rolls in
> enumerate(iter(roll, None)) if len(Counter(rolls)) == 1)
>
> Do I get bonus points for it being a one-liner that doesn't fit in
> eighty characters?

Lol.  You do not.  In fact, this should be syntax error :-D --- as I
guess it would be if it were a lambda expression?
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on the popularity of loops while and for

2021-09-02 Thread Hope Rouselle
Terry Reedy  writes:

> On 8/28/2021 9:31 AM, Hope Rouselle wrote:
>> I'd like get a statistic of how often each loop is used in practice.
>
> My guess is that for loops are at least twice as common as while loops.

Scanning just the Python 3.9.6's Lib/ directory --- using the ast module
and the program someone kindly posted here ---, I found for loop
appearing 598% more times than while loops.  The ratio was something
like that 6.98..., excluding files that due to encoding errors (or
whatever) could not be parsed.  (I reported these numbers elsewhere in
this thread.)

>> I was trying to take a look at the Python's standard libraries --- those
>> included in a standard installation of Python 3.9.6, say --- to see
>> which loops are more often used among while and for loops.  Of course,
>> since English use the preposition ``for'' a lot, that makes my life
>> harder.  Removing comments is easy, but removing strings is harder.  So
>> I don't know yet what I'll do.
>
> Try something like
>
> fors = 0
> for file in files:
> for line in file:
> if re.match(r"/s*for .* in ", line):
> fors += 1
>
> This excludes comprehensions, which have replaced some for loops.

That's nice too.  Thanks for the alternative.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on the popularity of loops while and for

2021-09-02 Thread Hope Rouselle
Chris Angelico  writes:

> On Sun, Aug 29, 2021 at 7:40 AM Hope Rouselle  wrote:
>>
>> I'd like get a statistic of how often each loop is used in practice.
>>
>> I was trying to take a look at the Python's standard libraries --- those
>> included in a standard installation of Python 3.9.6, say --- to see
>> which loops are more often used among while and for loops.  Of course,
>> since English use the preposition ``for'' a lot, that makes my life
>> harder.  Removing comments is easy, but removing strings is harder.  So
>> I don't know yet what I'll do.
>>
>> Have you guys ever measured something like that in a casual or serious
>> way?  I'd love to know.  Thank you!
>
> For analysis like this, I recommend using the Abstract Syntax Tree:
>
> https://docs.python.org/3/library/ast.html
>
> You can take a Python source file, parse it to the AST, and then walk
> that tree to see what it's using. That will avoid any false positives
> from the word "for" coming up in the wrong places.

Wonderful.  Someone here actually posted a nice ``routine'' that does
the job marvelously well.  I had the impression the poster posted only
to solve my request --- impressive and appreciated.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on writing a while loop for rolling two dice

2021-09-02 Thread Hope Rouselle
David Raymond  writes:

>> def how_many_times():
>>   x, y = 0, 1
>>   c = 0
>>   while x != y:
>> c = c + 1
>> x, y = roll()
>>   return c, (x, y)
>
> Since I haven't seen it used in answers yet, here's another option using our 
> new walrus operator
>
> def how_many_times():
> roll_count = 1
> while (rolls := roll())[0] != rolls[1]:
> roll_count += 1
> return (roll_count, rolls)

That's nice, although it doesn't seem more readable to a novice seeing a
while for the first time, seeing a loop for the first time, than that
while-True version.  In fact, I think the while-True is the clearest so
far.  But it's always nice to spot a walrus in the wild!  (If you're
somewhere safe, that is.)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on writing a while loop for rolling two dice

2021-09-02 Thread Hope Rouselle
dn  writes:

> On 29/08/2021 08.46, Hope Rouselle wrote:
>> Here's my solution:
>> 
>> --8<---cut here---start->8---
>> def how_many_times():
>>   x, y = 0, 1
>>   c = 0
>>   while x != y:
>> c = c + 1
>> x, y = roll()
>>   return c, (x, y)
>
>> 
>> Why am I unhappy?  I'm wish I could confine x, y to the while loop.  The
>> introduction of ``x, y = 0, 1'' must feel like a trick to a novice.  How
>> would you write this?
>
>> r...@zedat.fu-berlin.de (Stefan Ram) writes:
>>> """Rolls two dice until both yield the same value.
>>> Returns the number of times the two dice were rolled
>>> and the final value yielded."""
>>> roll_count = 0
>>> while True:
>>> outcome = roll_two_dice()
>>> roll_count += 1
>>> if outcome[ 0 ]== outcome[ 1 ]: break
>>> return roll_count, outcome[ 0 ]
>> 
>> You totally convinced me.  Thanks.
>
> On the other hand...
> whilst you expressed concern about the apparently disconnected 'set up'
> necessary before the loop, this solution adds a "True/Forever" and a
> "Break" construct, which some may deem not that much better (if at all)
>
> The idea of abrogating the while-condition but then adding another
> (disconnected) condition to break, seems to hold equal potential for
> confusion. or the type of dissatisfaction which motivated the original
> question!

Pretty well observed!  Hats to you.

> Looking at that from the inside-out, the loop's contents perform two
> functions: the rolling and counting (per Statement of Requirements), but
> also a loop-controlling element. Thus the reader's question: "what does
> this loop do?" is conflated with "how many times does it do it?".

Well put.

> Let's go completely off-the-rails, why not use a never-ending range() to
> fuel a for-loop 'counter', and within that loop perform the dice-roll(s)
> and decide if it is time to 'break'. The range replaces the "True". The
> for-loops index or 'counter' will deliver the desired result.
>
> Neat? No!
> Readable? No!
> An improvement over the while-True? Definitely not!
> Yet, the mechanism is the same AND offers a built-in counter. Hmmm...

Yeah.  Here's a little context.  I came across this by processing a list
of exercises.  (I'm teaching a course --- you know that by now, I
guess.)  So the first thing I observed was the equal volume of work
dedicated to while loops and for loops --- so I decided to compared
which appeared more often in a certain sample of well-written Python
code.  It turns out the for loop was much more frequent.  Students have
been reporting too much work in too little time, so I decided to reduce
the number of exercises involving while loops.  When I began to look at
the exercises, to see which ones I'd exclude, I decided to exclude them
all --- lol! --- except for one.  The one that remained was this one
about rolling dice until a satisfying result would appear.  (All other
ones were totally more naturally written with a for loop.)

So if I were to also write this with a for-loop, it'd defeat the purpose
of the course's moment.  Besides, I don't think a for-loop would improve
the readability here.

But I thought your protest against the while-True was very well put:
while-True is not too readable for a novice.  Surely what's readable or
more-natural /to someone/ is, well, subjective (yes, by definition).
But perhaps we may agree that while rolling dice until a certain
success, we want to roll them while something happens or doesn't happen.
One of the two.  So while-True is a bit of a jump.  Therefore, in this
case, the easier and more natural option is to say while-x-not-equal-y.

But this approach seems to force me into initializing x, y with
different values.

> Returning to the concern:
>
> x, y = 0, 1
> c = 0
>
> The first line is purely to ensure that the loop executes at least once,
> ie the two assigned-values are not 'real'. Hence the disquiet!
>
> Initiating the counter is unavoidable (@Chris' suggestion notwithstanding).
>
> However, remember that Python (like decent DBs) has a concept (and an
> idiom) of a value to be used when we don't (yet) know what the value
> is/should be! Further that Python allows such a value to be used in
> comparisons:
>
>>>> None != None
> False
>>>> None == None
> True
>
> Leading to:
>
> c, x, y = 0, None, None
> while ...
>
>
> Which solution reverts to the original loop-contents. which seem more
> obvious and thus more readable. (YMMV!)
>
> Simplicity over 'being clever'...

I don't see it.  You seem to have found what we seem to agre

on floating-point numbers

2021-09-02 Thread Hope Rouselle
Just sharing a case of floating-point numbers.  Nothing needed to be
solved or to be figured out.  Just bringing up conversation.

(*) An introduction to me

I don't understand floating-point numbers from the inside out, but I do
know how to work with base 2 and scientific notation.  So the idea of
expressing a number as 

  mantissa * base^{power}

is not foreign to me. (If that helps you to perhaps instruct me on
what's going on here.)

(*) A presentation of the behavior

>>> import sys
>>> sys.version
'3.8.10 (tags/v3.8.10:3d8993a, May  3 2021, 11:48:03) [MSC v.1928 64 bit 
(AMD64)]'

>>> ls = [7.23, 8.41, 6.15, 2.31, 7.73, 7.77]
>>> sum(ls)
39.594

>>> ls = [8.41, 6.15, 2.31, 7.73, 7.77, 7.23]
>>> sum(ls)
39.61

All I did was to take the first number, 7.23, and move it to the last
position in the list.  (So we have a violation of the commutativity of
addition.)

Let me try to reduce the example.  It's not so easy.  Although I could
display the violation of commutativity by moving just a single number in
the list, I also see that 7.23 commutes with every other number in the
list.

(*) My request

I would like to just get some clarity.  I guess I need to translate all
these numbers into base 2 and perform the addition myself to see the
situation coming up?
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on the popularity of loops while and for

2021-09-02 Thread Hope Rouselle
Barry  writes:

>> On 28 Aug 2021, at 22:42, Hope Rouselle  wrote:
>> 
>> I'd like get a statistic of how often each loop is used in practice.  
>> 
>> I was trying to take a look at the Python's standard libraries --- those
>> included in a standard installation of Python 3.9.6, say --- to see
>> which loops are more often used among while and for loops.  Of course,
>> since English use the preposition ``for'' a lot, that makes my life
>> harder.  Removing comments is easy, but removing strings is harder.  So
>> I don't know yet what I'll do.
>> 
>> Have you guys ever measured something like that in a casual or serious
>> way?  I'd love to know.  Thank you!
>
> I am interesting in why you think that choice of while vs. for is
> about popularity?

Perhaps you think my choice of "popular" had a special judgement about
it.  Let's define "popular" in the way that I used it.  

--8<---cut here---start->8---
Definition.  We say x is more popular than y if x appears more times
relative to y when considered in a sample.

Example.  For loops are more popular than while loops in a certain
sample because they appeared 17 times compared to 5 times the while loop
appeared.
--8<---cut here---end--->8---

So it's just a numeric comparison.

[...]
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on the popularity of loops while and for

2021-08-28 Thread Hope Rouselle
r...@zedat.fu-berlin.de (Stefan Ram) writes:

> Hope Rouselle  writes:
>>Have you guys ever measured something like that in a casual or serious
>
> import ast
> import pathlib
> rootname=r''
> rootpath=pathlib.Path(rootname)
> rootiterable=rootpath.glob('**/*.py')
> first = True
> WhileCount = 0
> ForCount = 0
> for filepath in rootiterable:
> try:
> with filepath.open(encoding="utf-8") as file:
> source=file.read()
> parse=ast.parse(source, filename=str(file))
> for entry in ast.walk(parse):
> if isinstance(entry, ast.While):
> WhileCount+=1
> print( f"{ForCount=}, {WhileCount=}" )
> elif isinstance(entry, ast.For):
> ForCount+=1
> print( f"{ForCount=}, {WhileCount=}" )
> except SyntaxError as inst:
>if first:
>print( f"{sys.exc_info()[ 0 ] =}" )
>print( f"{type( inst ) =}" )
>print( f"{inst.args =}" )
>print( f"{inst =}" )
>print( f"skipping {filepath}." )
> first=False

You are so wonderful!  Thanks quite a lot.  Here's what I got:

  ForCount=18703, WhileCount=2505

I have pretty much just the standard libraries and a couple more ---
sympy and some xlsxwriter library.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on writing a while loop for rolling two dice

2021-08-28 Thread Hope Rouselle
Hope Rouselle  writes:

> r...@zedat.fu-berlin.de (Stefan Ram) writes:
>
>> Hope Rouselle  writes:
>>>How would you write this?
>>
>> """Rolls two dice until both yield the same value.
>> Returns the number of times the two dice were rolled
>> and the final value yielded."""
>> roll_count = 0
>> while True:
>> outcome = roll_two_dice()
>> roll_count += 1
>> if outcome[ 0 ]== outcome[ 1 ]: break
>> return roll_count, outcome[ 0 ]
>
> You totally convinced me.  Thanks.

Wait, I'm surprised ``outcome'' is still a valid name at the
return-statement.  Wasn't it defined inside the while?  Shouldn't its
scope be restricted to the while block?  I had no idea.  I should learn
some Python.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on writing a while loop for rolling two dice

2021-08-28 Thread Hope Rouselle
r...@zedat.fu-berlin.de (Stefan Ram) writes:

> Hope Rouselle  writes:
>>How would you write this?
>
> """Rolls two dice until both yield the same value.
> Returns the number of times the two dice were rolled
> and the final value yielded."""
> roll_count = 0
> while True:
> outcome = roll_two_dice()
> roll_count += 1
> if outcome[ 0 ]== outcome[ 1 ]: break
> return roll_count, outcome[ 0 ]

You totally convinced me.  Thanks.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on writing a while loop for rolling two dice

2021-08-28 Thread Hope Rouselle
r...@zedat.fu-berlin.de (Stefan Ram) writes:

> Hope Rouselle  writes:
>>Wait, I'm surprised ``outcome'' is still a valid name at the
>>return-statement.  Wasn't it defined inside the while?  Shouldn't its
>>scope be restricted to the while block?  I had no idea.  I should learn
>>some Python.
>
>   In Python, local names can be introduced by an assignment
>   and have function scope. There is no block scope in Python.
>
>   Below, "name" /is/ a local name already, but is being 
>   used before being assigned to.
>
> def function():
> if False:
> name = 0
> return name
>
> function()
>
> # return name
> # UnboundLocalError: local variable 'name' referenced before assignment

I appreciated the example.  I had no idea.  (I had looked up the rules
and it was pretty simple to understand, but an example is always nice.)
Thank you so much.
-- 
https://mail.python.org/mailman/listinfo/python-list


on the popularity of loops while and for

2021-08-28 Thread Hope Rouselle
I'd like get a statistic of how often each loop is used in practice.  

I was trying to take a look at the Python's standard libraries --- those
included in a standard installation of Python 3.9.6, say --- to see
which loops are more often used among while and for loops.  Of course,
since English use the preposition ``for'' a lot, that makes my life
harder.  Removing comments is easy, but removing strings is harder.  So
I don't know yet what I'll do.

Have you guys ever measured something like that in a casual or serious
way?  I'd love to know.  Thank you!
-- 
https://mail.python.org/mailman/listinfo/python-list


on writing a while loop for rolling two dice

2021-08-28 Thread Hope Rouselle
How should I write this?  I'd like to roll two six-sided dice until I
get the same number on both.  I'd like to get the number of times I
tried.  Here's a primitive I'm using:

--8<---cut here---start->8---
>>> x, y = roll()
>>> x
6
>>> y
6 # lucky

>>> x, y = roll()
>>> x
4
>>> y
1 # unlucky
--8<---cut here---end--->8---

Here's my solution:

--8<---cut here---start->8---
def how_many_times():
  x, y = 0, 1
  c = 0
  while x != y:
c = c + 1
x, y = roll()
  return c, (x, y)
--8<---cut here---end--->8---

Why am I unhappy?  I'm wish I could confine x, y to the while loop.  The
introduction of ``x, y = 0, 1'' must feel like a trick to a novice.  How
would you write this?  Thank you!
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-08-28 Thread Hope Rouselle
"Peter J. Holzer"  writes:

> On 2021-08-22 16:28:12 -0300, Hope Rouselle wrote:
>> I have a certain distaste for syntax too.  For instance, I would much
>> rather write and read ``first(ls)'' than ``ls[0]''.
>
> Would you also prefer `twothousandthreehundredandtwentythird(ls)` over
> `ls[2322]`?

Omg, I'm afraid you are serious! :-D  We could use something like

  list_get(ls, 2322).

Of course that ls[2322] is perfect fine.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-08-23 Thread Hope Rouselle
Dennis Lee Bieber  writes:

> On Sat, 21 Aug 2021 17:15:14 -0300, Hope Rouselle 
> declaimed the following:
>
>>write some PHP precisely because it looked so much more cryptic than
>>Allaire ColdFusion.  Then C looked even more cryptic, so I fell in love
>>with C.
>>
>   Try APL then...

Lol.  If the code below is APL, then thanks, but no, thanks. :-) We
change over the years.  These days I'm more or less obsessed with
clarity.  For instance, I'm a lot into literate programming Knuth-style.

I have a certain distaste for syntax too.  For instance, I would much
rather write and read ``first(ls)'' than ``ls[0]''.  I can't see too
much advantage in using more syntax for something that procedures could
do.  But, of course, in certain contexts it might be better to write
ls[0] or something like that.  In these cases, the writer could then
make the case and then define the new syntax, so I do think that writers
should have the power to change syntax.

I'm well aware of the double-edged sword this is.  (Incidentally,
yesterday I was happily reading a Python programmer talking about [1]
how Dijkstra got it right for suggesting we shouldn't be using goto.
Knuth had the opinion that it'd be a shame to remove goto, even though,
of course, he'd agree that people should learn how to use it.)

Is syntax the same thing as goto?  I'm not sure.  If it is, then I am
like Knuth --- I feel sad when languages remove my ability to change
syntax.  But surely I feel overwhelmed sometimes when there's too much
syntax created by the programmer who wrote the library that I'm trying
to understand.

(*) Footnotes

[1] Notes on structured concurrency
https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful

> (I suspect this will get garbaged in processing...)
>
>   4 5 ? 20 ? 52
>
> (it did... the first ? [in my display] is Greek lower case rho, the second
> question mark is... a question mark).

Like you, I see two question marks.

>   4 5 $RHO 20 ? 52
>
>   Generate 20 random numbers in the range 1..52, no duplicates, reshape
> the vector into a 4x5 matrix.
>
>   That just "dealt" four poker hands (needs logic to translate integer
> 1..52 into suit/rank).

That's wild. :-) Was this created by Brian Kernighan?  It's hard to
believe.  Oh, I think he wrote AMPL, wasn't it?  A Mathematical
Programming Language, or something like that.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-08-22 Thread Hope Rouselle
Hope Rouselle  writes:

> Chris Angelico  writes:
>
>> On Tue, Aug 17, 2021 at 4:02 AM Greg Ewing
>>  wrote:
>>> The second best way would be to not use import_module, but to
>>> exec() the student's code. That way you don't create an entry in
>>> sys.modules and don't have to worry about somehow unloading the
>>> module.
>>
>> I would agree with this. If you need to mess around with modules and
>> you don't want them to be cached, avoid the normal "import" mechanism,
>> and just exec yourself a module's worth of code.
>
> Sounds like a plan.  Busy, haven't been able to try it out.  But I will.
> Soon.  Thank you!

Just to close off this thread, let me share a bit of what I wrote.  The
result is a lot better.  Thanks for all the help!

I exec the student's code into a dictionary g.

--8<---cut here---start->8---
def fs_read(fname):
  with open(fname, "r") as f:
return f.read()

def get_student_module_exec(fname):
  g = {}
  try:
student_code = fs_read(fname)
student = exec(student_code, g)
  except Exception as e:
return False, str(e)
  return True, g

def get_student_module(fname):
  return get_student_module_exec(fname)
--8<---cut here---end--->8---

And now write the test's key as if I were a student and named my test as
"test_key.py".

--8<---cut here---start->8---
def get_key():
  okay, k = get_student_module("test_key.py")
  if not okay:
# Stop everything.
...
  return g
--8<---cut here---end--->8---

The procedure for grading a question consumes the student's code as a
dictionary /s/, grabs the key as /k/ and checks whether the procedures
are the same.  So, suppose I want to check whether a certain function
/fn/ written in the student's dictionary-code /s/ matches the key's.
Then I invoke check_student_procedure(k, s, fn).

--8<---cut here---start->8---
def check_student_procedure(k, s, fn, args = [], wrap = identity):
  return check_functions_equal(g[fn], s.get(fn, None), args, wrap)
--8<---cut here---end--->8---

For completeness, here's check_functions_equal.

--8<---cut here---start->8---
def check_functions_equal(fn_original, fn_candidate, args = [], wrap = 
identity):
  flag, e = is_function_executable(fn_candidate, args)
  if not flag:
return False, "runtime", e
  # run original and student's code, then compare them
  answer_correct = fn_original(*args)
  answer_student = wrap(fn_candidate(*args))
  if answer_correct != answer_student:
return False, None, str(answer_student)
  return True, None, None

def identity(x):
  return x
--8<---cut here---end--->8---

To explain my extra complication there: sometimes I'm permissive with
student's answers.  Suppose a question requires a float as an answer but
in some cases the answer is a whole number --- such as 1.0.  If the
student ends up producing an int, the student gets that case right: I
wrap the student's answer in a float() and the check turns out
successful.

I probably don't need to check whether a procedure is executable first,
but I decided to break the procedure into two such steps.

--8<---cut here---start->8---
def is_function_executable(f, args = []):
  try:
f(*args)
  except Exception as e:
return False, str(e)
  return True, None
--8<---cut here---end--->8---
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-08-21 Thread Hope Rouselle
Chris Angelico  writes:

> On Sun, Aug 22, 2021 at 4:37 AM Hope Rouselle  wrote:
>>
>> Greg Ewing  writes:
>>
>> > On 21/08/21 1:36 pm, Hope Rouselle wrote:
>> >> I wish I could restrict their syntax too, though, but I fear that's
>> >> not possible.  For instance, it would be very useful if I could
>> >> remove loops.
>> >
>> > Actually you could, using ast.parse to get an AST and then walk
>> > it looking for things you don't want to allow.
>>
>> Very interesting!  Thanks very much.  That would let me block them,
>> though the ideal would be a certain python.exe binary that simply blows
>> a helpful syntax error when they use something the course doesn't allow.
>> But surely the course could provide students with a certain module or
>> procedure which would validate their work.  (Don't turn in unless you
>> pass these verifications.)
>>
>> > You could also play around with giving them a custom set of
>> > builtins and restricting what they can import. Just be aware
>> > that such things are not foolproof, and a sufficiently smart
>> > student could find ways around them. (Although if they know
>> > that much they probably deserve to pass the course anyway!)
>>
>> So true!  If they can get around such verifications, they should present
>> their work at an extra-curricular sessions.
>
> Agreed... if they do it knowingly. On the other hand, if they just
> turn in code copied blindly from Stack Overflow...

When a student does something impressive, we should recognize it.
(Don't we do this with ourselves?  That's why we should.  Not because
it's a strategy to sort of encourage the behavior, but because it is our
culture.)  If if turns out they're clueless about their own feat, we
probably would notice by recognizing it in the first time.  Being so
cool, they could present it to everyone.  (This is not a strategy to
sort of catch plagiarism, but rather a sober modus operandi that seems
to work well.)  Copying blindly from somewhere could be the event that
triggers a deep interest.

One of the things that attracted me to computers was the keyboard.  Not
sure if it was the first thing.  But it's pretty.  One time I saw
Microsoft Windows and someone let me play on Paint.  I loved the BĂ©zier
curves and some of the colors.  Then came the Internet.  Wow!  The
Internet brought me UNIX and I began to see computer code.  I was
impressed at how someone could just understand such things.  I wanted to
write some PHP precisely because it looked so much more cryptic than
Allaire ColdFusion.  Then C looked even more cryptic, so I fell in love
with C.

Maybe a little copy from wherever could be the spark for bigger things.
Education is a fantastically difficult thing and making mistakes seems
so involved with it that it's perhaps undetachable from it.  Of course,
we should stop ourselves from making irreversible mistakes such as
falling from a precipice.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-08-21 Thread Hope Rouselle
Greg Ewing  writes:

> On 21/08/21 1:36 pm, Hope Rouselle wrote:
>> I wish I could restrict their syntax too, though, but I fear that's
>> not possible.  For instance, it would be very useful if I could
>> remove loops.
>
> Actually you could, using ast.parse to get an AST and then walk
> it looking for things you don't want to allow.

Very interesting!  Thanks very much.  That would let me block them,
though the ideal would be a certain python.exe binary that simply blows
a helpful syntax error when they use something the course doesn't allow.
But surely the course could provide students with a certain module or
procedure which would validate their work.  (Don't turn in unless you
pass these verifications.)

> You could also play around with giving them a custom set of
> builtins and restricting what they can import. Just be aware
> that such things are not foolproof, and a sufficiently smart
> student could find ways around them. (Although if they know
> that much they probably deserve to pass the course anyway!)

So true!  If they can get around such verifications, they should present
their work at an extra-curricular sessions.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on slices, negative indices, which are the equivalent procedures?

2021-08-20 Thread Hope Rouselle
Hope Rouselle  writes:

> Hope Rouselle  writes:
>
>> Dennis Lee Bieber  writes:
>>
>>> On Sun, 15 Aug 2021 00:05:29 -0300, Jack Brandom 
>>> declaimed the following:
>>>
>>>>Dennis Lee Bieber  writes:
>>>>
>>>
>>>>> subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]
>>>>> sliceop: ':' [test]
>>>
>>>>
>>>>This is looking less readable, so, no, I prefer that previous, which was
>>>>much clearer about slices.  I can't even quite make the slices out in
>>>>these rules.  (But thanks for the opportunity to try.)
>>>
>>> The last part of "subscript" has the main "slice" notation (stripping
>>> the quotes)
>>>
>>> [test] : [test]
>>>
>>> and the "stride" component is [sliceop] being
>>>
>>> : [test]
>>
>> Let's see.  Let ``-->'' mean ``expands to''.  Then
>>
>>   subscript 
>>   --> [test] ':' [test] [sliceop]
>>   --> [test] ':' [test] ':' [test]
>>
>> Does the brackets mean that its content count be omitted?
>   ^
>
> _Do_ the brackets mean that its content _can_ be omitted?  (This is
  ^^^
> really too much coffee.  It's crazy where my attention goes.)

Lol.  Should I say _their_ content?  I guess so. :-) 
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on slices, negative indices, which are the equivalent procedures?

2021-08-20 Thread Hope Rouselle
Hope Rouselle  writes:

> Dennis Lee Bieber  writes:
>
>> On Sun, 15 Aug 2021 00:05:29 -0300, Jack Brandom 
>> declaimed the following:
>>
>>>Dennis Lee Bieber  writes:
>>>
>>
>>>> subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]
>>>> sliceop: ':' [test]
>>
>>>
>>>This is looking less readable, so, no, I prefer that previous, which was
>>>much clearer about slices.  I can't even quite make the slices out in
>>>these rules.  (But thanks for the opportunity to try.)
>>
>>  The last part of "subscript" has the main "slice" notation (stripping
>> the quotes)
>>
>>  [test] : [test]
>>
>> and the "stride" component is [sliceop] being
>>
>>  : [test]
>
> Let's see.  Let ``-->'' mean ``expands to''.  Then
>
>   subscript 
>   --> [test] ':' [test] [sliceop]
>   --> [test] ':' [test] ':' [test]
>
> Does the brackets mean that its content count be omitted?
  ^

_Do_ the brackets mean that its content _can_ be omitted?  (This is
really too much coffee.  It's crazy where my attention goes.)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-08-20 Thread Hope Rouselle
Greg Ewing  writes:

> On 21/08/21 6:15 am, Hope Rouselle wrote:
>>>>> code()
>> 'def p():\n  import math\n  return math.e\n'
>>>>> exec(code())
>>>>> p
>> 
>>>>> p()
>> 2.718281828459045
>
> Note that this pollutes the globals of the module that you're calling
> exec() from. For better isolation you can pass in an explicit globals
> dict:
>
> g = {}
> exec(code(), g)
> g['p']()

Oh!  Now I understand how to use it!  That's in fact what I was looking
for.  I noticed it was polluting my environment and was thinking --- hm,
that's no good.  Thank you.

So I believe I understand how to pollute their environment too.  Say I
have a procedure called external that I'd like to make available to
them.  It seems this is what I need to do.

--8<---cut here---start->8---
def external():
  return "external"

def run():
  s = """
def s(*args):
  import math
  return external(), math.e, args
"""
  g = {}
  exec(s, g)
  g["external"] = external
  return g
--8<---cut here---end--->8---

>>> student["s"](1, 2)
('external', 2.718281828459045, (1, 2))

That's good.  So I can restrict their environment too, by removing some
built-ins and so on.  (I wish I could restrict their syntax too, though,
but I fear that's not possible.  For instance, it would be very useful
if I could remove loops.  If the course doesn't let them use them, it is
silly to have to ask them kindly not to use them --- please be patient
with this poorly designed course.  In reality there is a whole
department learning to run a course and there are many students helping
this department get a passing grade into how to do it.) :-D

Anyhow, your ideas have improved the outlook of this grader quite a lot.
Thank you!
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-08-20 Thread Hope Rouselle
Chris Angelico  writes:

> On Tue, Aug 17, 2021 at 4:02 AM Greg Ewing  
> wrote:
>> The second best way would be to not use import_module, but to
>> exec() the student's code. That way you don't create an entry in
>> sys.modules and don't have to worry about somehow unloading the
>> module.
>
> I would agree with this. If you need to mess around with modules and
> you don't want them to be cached, avoid the normal "import" mechanism,
> and just exec yourself a module's worth of code.

This is looks very interesting.

--8<---cut here---start->8---
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:01:18) [MSC v.1900 32 bit 
(Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> >>> code

>>> code()
'def p():\n  import math\n  return math.e\n'
>>> p
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'p' is not defined
>>> exec(code())
>>> p

>>> p()
2.718281828459045
--8<---cut here---end--->8---

This is also totally unsafe, right?  But at least it won't give me those
loading problems I had before.  But I think I'm gonna go with Greg
Ewing's first option --- running it all on a separate process, computing
that report I was computing, then printing it out to the stdout and
reading it from a controller process to know the student's results.

This way I'm one step closer to safety --- I could chroot it and give it
a limited period of time for execution.  I suppose I could also
customize Python's environment to only allow the modules I'd like to
allow as well as the builtins I'd like to allow.  (That would be
interesting as well.)

So, alright, let me learn how to spawn a process.  Thanks very much.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on slices, negative indices, which are the equivalent procedures?

2021-08-20 Thread Hope Rouselle
Dennis Lee Bieber  writes:

> On Sun, 15 Aug 2021 00:05:29 -0300, Jack Brandom 
> declaimed the following:
>
>>Dennis Lee Bieber  writes:
>>
>
>>> subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]
>>> sliceop: ':' [test]
>
>>
>>This is looking less readable, so, no, I prefer that previous, which was
>>much clearer about slices.  I can't even quite make the slices out in
>>these rules.  (But thanks for the opportunity to try.)
>
>   The last part of "subscript" has the main "slice" notation (stripping
> the quotes)
>
>   [test] : [test]
>
> and the "stride" component is [sliceop] being
>
>   : [test]

Let's see.  Let ``-->'' mean ``expands to''.  Then

  subscript 
  --> [test] ':' [test] [sliceop]
  --> [test] ':' [test] ':' [test]

Does the brackets mean that its content count be omitted?
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-20 Thread Hope Rouselle
Dennis Lee Bieber  writes:

> On Sun, 15 Aug 2021 00:15:58 -0300, Hope Rouselle 
> declaimed the following:
>
>   Giganews seems to have just vomited up three days worth of traffic...
>
>>Dennis Lee Bieber  writes:
>>
>>>
>>> Granted, the fact that the Amiga used a shared common address space for
>>> all running applications made IPC quite easy -- one looked up the
>>> application message port, then added a small packet to the linked list
>>> associated with the port. That small packet basically held the address of
>>> the message port for returning data, and the address of the data being
>>> passed. The closet thing I've seen to that capability on systems with
>>> process-isolated virtual memory is (Open)VMS "mailbox" structures. The
>>> difference being that the entire data had to be written (QIO) to the
>>> mailbox, and the receiver had to read (another QIO call) the message --
>>> this allowed the address space to change.
>>>
>>> I've not seen anything equivalent in my light perusal of the Win32 API
>>> (the various guide books aren't layed out in any way to be a reference),
>>> and Linux seems to use UNIX sockets for IPC... No way to search for a
>>> connection point by name...
>>
>>I don't know anything about Amiga, REXX et cetera, so I might be totall
>>off here. But since you spoke of your perusal of the Win32 API, let me
>>add a tiny bit.  I gave myself a quick tour through the Win32 API using
>>Pavel Yosifovich's book ``Windows 10 System Programming''.  It's a two
>>volume work.  The thing that impressed me the most was the many ways to
>>do the IPC.  The purpose the work is clearly to show what is available
>>and it it probably does the job well.  (I ignored Windows for most of my
>>life and now I decided to take a look at it.  I don't feel it has much
>>of the elegance of UNIX.  It's what it is.)
>
>   For a book that was published only a year ago -- it seems to be hard to
> find... Amazon has v1, but not v2.

Indeed.  I believe it's because it's not quite released yet.  I had
found it here:

  https://leanpub.com/windows10systemprogrammingpart2

And this seems to be a sort-of-early-release.  I'm not really sure.

>   I have the "Windows Internals 6th" both volumes.
>
>   The closet those get to IPC is something called ALPC -- and that is
> declared to be internal only, not available to third party programmers.
>
>   "Programming Windows 6th" is focused on using C# (which, for the most
> part, means using the "managed" API, not the Win32 API directly).

Makes sense.

>   "Mailslots" (I'm using Google to find Windows IPC options) initially
> sound close to Amiga message ports... Except there is no status return
> inherent to the system; Amiga messages contain a return address so a
> program can wait for the status of processing by the destination address.
> They also don't qualify as VMS mailboxes as there can only be one reader
> (the creator of the mailslot). VMS mailboxes allow multiple writers and/or
> multiple readers.
>
>   "File mapping" doesn't provide any form of queuing -- multiple
> "writers" would require manually implementing some protocol to track "next
> available space" in the mapped file. It would allow bidirectional message
> passing, but again that requires some rather nasty overhead information
> (when is the return status valid -- it is presumed the client wanting the
> return status would get it from the same memory space as the message it
> sent, and that space can't be reused until the client explicitly frees it
> -- again by some overhead protocol changing headers of free space.
>
>   "Data Copy" also sounds close to Amiga message ports (closer than
> "Mailslots"), but the online documentation doesn't indicate if it queues
> messages. The documentation doesn't illustrate HOW to return TRUE or FALSE
> (processed/rejected). It advices that the receiver should treat the data as
> read-only -- though I have to wonder if it /could/ write a return status
> into the same message structure before "returning TRUE".
>
>   Named Pipes and Sockets both have the "tied to endpoints" problem, and
> in the case of Pipes, may only allow one sender to connect at a time
> (sockets are also one connection, but normally the receivers spawns a
> thread for each active connection and the connections are made using
> different allocated sockets).

Thanks for these summaries!
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-19 Thread Hope Rouselle
Chris Angelico  writes:

> On Tue, Aug 17, 2021 at 3:51 AM Hope Rouselle 
> wrote:
>>
>> Chris Angelico  writes:
>> >> Wow, I kinda feel the same as you here.  I think this justifies
>> >> perhaps
>> >> using a hardware solution.  (Crazy idea?! Lol.)
>> >
>> > uhhh Yes. Very crazy idea. Can't imagine why anyone would
>> > ever
>> > think about doing that.
>>
>> Lol.  Really?  I mean a certain panic button.  You know the GNU Emacs.
>> It has this queue with the implications you mentioned --- as much as it
>> can.  (It must of course get the messages from the system, otherwise it
>> can't do anything about it.)  And it has the panic button C-g.  The
>> keyboard has one the highest precedences in hardware interrupts,
>> doesn't
>> it not?  A certain very important system could have a panic button that
>> invokes a certain debugger, say, for a crisis-moment.
>>
>> But then this could be a lousy engineering strategy.  I am not an
>> expert
>> at all in any of this.  But I'm surprised with your quick
>> dismissal. :-)
>>
>> > Certainly nobody in his right mind would have WatchCat listening on
>> > the serial port's Ring Indicator interrupt, and then grab a paperclip
>> > to bridge the DTR and RI pins on an otherwise-unoccupied serial port
>> > on the back of the PC. (The DTR pin was kept high by the PC, and
>> > could
>> > therefore be used as an open power pin to bring the RI high.)
>>
>> Why not?  Misuse of hardware?  Too precious of a resource?
>>
>> > If you're curious, it's pins 4 and 9 - diagonally up and in from the
>> > short
>> > corner. 
>> > http://www.usconverters.com/index.php?main_page=page=61=0
>>
>> You know your pins!  That's impressive.  I thought the OS itself could
>> use something like that.  The fact that they never do... Says
>> something,
>> doesn't it?  But it's not too obvious to me.
>>
>> > And of COURSE nobody would ever take an old serial mouse, take the
>> > ball out of it, and turn it into a foot-controlled signal... although
>> > that wasn't for WatchCat, that was for clipboard management
>> > between my
>> > app and a Windows accounting package that we used. But that's a
>> > separate story.
>>
>> Lol.  I feel you're saying you would. :-)
>
> This was all a figure of speech, and the denials were all tongue in
> cheek. Not only am I saying we would, but we *did*. All of the above.

Cool! :-) 

> The Ring Indicator trick was one of the best, since we had very little
> other use for serial ports, and it didn't significantly impact the
> system during good times, but was always reliable when things went
> wrong.
>
> (And when I posted it, I could visualize the port and knew which pins
> to bridge, but had to go look up a pinout to be able to say their pin
> numbers and descriptions.)

Nice!

>> I heard of Python for the first time in the 90s.  I worked at an ISP.
>> Only one guy was really programming there, Allaire ColdFusion.  But,
>> odd enough, we used to say we would ``write a script in Python'' when
>> we meant to say we were going out for a smoke.  I think that was
>> precisely because nobody knew that ``Python'' really was.  I never
>> expected it to be a great language.  I imagined it was something like
>> Tcl.  (Lol, no offense at all towards Tcl.)
>
> Haha, that's a weird idiom!

Clueless people --- from Rio de Janeiro area in Brazil. :-)  It was
effectively just an in-joke.

> Funny you should mention Tcl.
>
> https://docs.python.org/3/library/tkinter.html

Cool!  Speaking of GUIs and Python, that Google software called Backup
and Sync (which I think it's about to be obsoleted by Google Drive) is
written in Python --- it feels a bit heavy.  The GUI too seems a bit
slow sometimes.  Haven't tried their ``Google Drive'' as a replacement
yet.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-08-19 Thread Hope Rouselle
Martin Di Paola  writes:

> This may not answer your question but it may provide an alternative 
> solution.
>
> I had the same challenge that you an year ago so may be my solution will 
> work for you too.
>
> Imagine that you have a Markdown file that *documents* the expected 
> results.
>
> This is the final exam, good luck!
>
> First I'm going to load your code (the student's code):
>
> ```python
 import student
> ```
>
> Let's see if you programmed correctly a sort algorithm
>
> ```python
 data = [3, 2, 1, 3, 1, 9]
 student.sort_numbers(data)
> [1, 1, 2, 3, 3, 9]
> ```
>
> Let's now if you can choose the correct answer:
>
> ```python
 t = ["foo", "bar", "baz"]
 student.question1(t)
> "baz"
> ```
>
> Now you can run the snippets of code with:
>
>byexample -l python the_markdown_file.md
>
> What byexample does is to run the Python code, capture the output and 
> compare it with the expected result.
>
> In the above example "student.sort_numbers" must return the list
> sorted.
> That output is compared by byexample with the list written below.
>
> Advantages? Each byexample run is independent of the other and the 
> snippet of codes are executed in a separated Python process. byexample 
> takes care of the IPC.
>
> I don't know the details of your questions so I'm not sure if byexample 
> will be the tool for you. In my case I evaluate my students giving them 
> the Markdown and asking them to code the functions so they return the 
> expected values.

Currently procedures in one question are used in another question.
Nevertheless, perhaps I could (in other tests) design something
different.  Although, to be honest, I would rather not have to use
something like Markdown because that means more syntax for students.

> Depending of how many students you have you may considere to
> complement this with INGInious. It is designed to run students'
> assignments assuming nothing on the untrusted code.
>
> Links:
>
> https://byexamples.github.io/byexample/
> https://docs.inginious.org/en/v0.7/

INGInious looks pretty interesting.  Thank you!
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-08-19 Thread Hope Rouselle
Chris Angelico  writes:

> On Tue, Aug 17, 2021 at 4:02 AM Greg Ewing
>  wrote:
>> The second best way would be to not use import_module, but to
>> exec() the student's code. That way you don't create an entry in
>> sys.modules and don't have to worry about somehow unloading the
>> module.
>
> I would agree with this. If you need to mess around with modules and
> you don't want them to be cached, avoid the normal "import" mechanism,
> and just exec yourself a module's worth of code.

Sounds like a plan.  Busy, haven't been able to try it out.  But I will.
Soon.  Thank you!
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-08-16 Thread Hope Rouselle
Hope Rouselle  writes:

> Hope Rouselle  writes:
>
> [...]
>
>> Of course, you want to see the code.  I need to work on producing a
>> small example.  Perhaps I will even answer my own question when I do.
>
> [...]
>
> Here's a small-enough case.  We have two students here.  One is called
> student.py and the other is called other.py.  They both get question 1
> wrong, but they --- by definition --- get question 2 right.  Each
> question is worth 10 points, so they both should get losses = 10.
>
> (*) Student student.py
>
> def question1(t): # right answer is t[2]
>   return t[1] # lack of attention, wrong answer
>
>
> (*) Student other.py
>
> def question1(t): # right answer is t[2]
>   return t[0] # also lack of attention, wrong answer
>
>
> (*) Grading
>
> All is good on first run.
>
> Python 3.5.2 [...] on win32
> [...]
>>>> reproducible_problem()
> student.py, total losses 10
> other.py, total losses 10
>
> The the problem:
>
>>>> reproducible_problem()
> student.py, total losses 0
> other.py, total losses 0
>
> They lose nothing because both modules are now permanently modified.
>
> (*) The code of grading.py
>
> # -*- mode: python; python-indent-offset: 2 -*-
> def key_question1(t): 
>   # Pretty simple.  Student must just return index 2 of a tuple.
>   return t[2]
>
> def reproducible_problem(): # grade all students
>   okay, m = get_student_module("student.py")
>   r = grade_student(m)
>   print("student.py, total losses", r) # should be 10
>   okay, m = get_student_module("other.py")
>   r = grade_student(m)
>   print("other.py, total losses", r) # should be 10
>
> def grade_student(m): # grades a single student
>   losses  = question1_verifier(m)
>   losses += question2_verifier(m)
>   return losses
>
> def question1_verifier(m):
>   losses = 0
>   if m.question1( (0, 1, 2, 3) ) != 2: # wrong answer
> losses = 10
>   return losses
>
> def question2_verifier(m):
>   m.question1 = key_question1
>   # To grade question 2, we overwrite the student's module by giving
>   # it the key_question1 procedure.  This way we are able to let the
>   # student get question 2 even if s/he got question 1 incorrect.
>   losses = 0
>   return losses 

My solution is to painfully save each original procedure from student in
a variable, replace them with the key's, then restore them at the end.
I couldn't overwrite m.__dict__ because it is readonly.  (Didn't find a
neater's way out.)

def question2_verifier(m):
  question1_original = m.question1
  m.question1 = key_question1
  # apply verifications... 
  m.question1 = question1_original
  return losses
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-08-16 Thread Hope Rouselle
Hope Rouselle  writes:

[...]

> Of course, you want to see the code.  I need to work on producing a
> small example.  Perhaps I will even answer my own question when I do.

[...]

Here's a small-enough case.  We have two students here.  One is called
student.py and the other is called other.py.  They both get question 1
wrong, but they --- by definition --- get question 2 right.  Each
question is worth 10 points, so they both should get losses = 10.

(*) Student student.py

--8<---cut here---start->8---
def question1(t): # right answer is t[2]
  return t[1] # lack of attention, wrong answer
--8<---cut here---end--->8---

(*) Student other.py

--8<---cut here---start->8---
def question1(t): # right answer is t[2]
  return t[0] # also lack of attention, wrong answer
--8<---cut here---end--->8---

(*) Grading

All is good on first run.

Python 3.5.2 [...] on win32
[...]
>>> reproducible_problem()
student.py, total losses 10
other.py, total losses 10

The the problem:

>>> reproducible_problem()
student.py, total losses 0
other.py, total losses 0

They lose nothing because both modules are now permanently modified.

(*) The code of grading.py

--8<---cut here---start->8---
# -*- mode: python; python-indent-offset: 2 -*-
def key_question1(t): 
  # Pretty simple.  Student must just return index 2 of a tuple.
  return t[2]

def reproducible_problem(): # grade all students
  okay, m = get_student_module("student.py")
  r = grade_student(m)
  print("student.py, total losses", r) # should be 10
  okay, m = get_student_module("other.py")
  r = grade_student(m)
  print("other.py, total losses", r) # should be 10

def grade_student(m): # grades a single student
  losses  = question1_verifier(m)
  losses += question2_verifier(m)
  return losses

def question1_verifier(m):
  losses = 0
  if m.question1( (0, 1, 2, 3) ) != 2: # wrong answer
losses = 10
  return losses

def question2_verifier(m):
  m.question1 = key_question1
  # To grade question 2, we overwrite the student's module by giving
  # it the key_question1 procedure.  This way we are able to let the
  # student get question 2 even if s/he got question 1 incorrect.
  losses = 0
  return losses 

def get_student_module(fname): 
  from importlib import import_module
  mod_name = basename(fname)
  try:
student = import_module(mod_name)
  except Exception as e:
return False, str(e)
  return True, student

def basename(fname): # drop the the .py extension
  return "".join(fname.split(".")[ : -1])
--8<---cut here---end--->8---
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on perhaps unloading modules?

2021-08-16 Thread Hope Rouselle
Greg Ewing  writes:

> On 16/08/21 1:50 am, Hope Rouselle wrote:
>> By the way, I'm aware that what I'm doing here is totally unsafe and I
>> could get my system destroyed.  I'm not planning on using this --- thank
>> you for your concern.  I'm just interested in understanding more about
>> modules.
>
> Okay, I'll assume all the security issues have been taken are of, e.g.
> by running all of this in a virtual machine...

Oh, good idea. :-D

>> Notice how student m0 (who really scored a zero)
>> first gets his grade right, but if I invoke it again, then it gets 50.0.
>
> The best way to do this would be to run each student's file in
> a separate process, so you know you're getting a completely fresh
> start each time.

Yes, that would be much better indeed, but I'd be left with IPC
mechanisms of exchanging data.  My first idea is to just print out a
JSON of the final report to be read by the grader-process.  (That is
essentially what I'm already doing, except that I don't need to
serialize things in a string.)

> The second best way would be to not use import_module, but to
> exec() the student's code. That way you don't create an entry in
> sys.modules and don't have to worry about somehow unloading the
> module.
>
> Something like
>
> code = read_student_file(student_name)
> env = {} # A dict to hold the student's module-level definitions
> exec(code, env)
> grade_question1(env)
> env['procedure_x'] = key.procedure_x
> grade_question2(env)
> ...etc...

That seems a lot easier to implement.

>> That's not the whole problem.  For reasons I don't understand, new
>> modules I load --- that is, different students --- get mixed with these
>> modifications in m0 that I made at some point in my code.
>
> I would have to see a specific example of that. 

I'm sorry.  I don't think I was right in that observation.  I tried to
produce one such small program to post here and failed.  (Let's forget
about that.)

> One thing to keep in mind is that if key.procedure_x modifies any
> globals in the key module, it will still modify globals in the key
> module -- not the student's module -- after being transplanted there.
>
> More generally, there are countless ways that a student's code
> could modify something outside of its own module and affect the
> behaviour of other student's code. This is why it would be
> vastly preferable to run each test in a fresh process.

I'm totally convinced.  I'll do everything in a fresh process.  Having
guarantees makes the job much more pleasant to do.

>> (*) If it were easy to unload modules...
>
> It's sometimes possible to unload and reimport a module, but
> only if the module's effects are completely self-contained.
> That depends not only on what the module itself does, but
> what other modules do with it. If any other module has imported
> it, that module will still contain references to the old
> module; if there are instances of a class defined in it still
> existing, they will still be instances of the old version of
> the class; etc.
>
> 99.999% of the time it's easier to just start again with a
> fresh Python process.

I'm totally convinced.  Thank you so much!
-- 
https://mail.python.org/mailman/listinfo/python-list


on perhaps unloading modules?

2021-08-16 Thread Hope Rouselle
(*) Introduction

By the way, I'm aware that what I'm doing here is totally unsafe and I
could get my system destroyed.  I'm not planning on using this --- thank
you for your concern.  I'm just interested in understanding more about
modules.

(*) The problem

I got myself into a mess with loading modules at runtime.  The reason
I'm loading modules at runtime is because I'm writing a script to grade
students' work.  (Each student test is a file.py.)

Here's the phenomenon.  Notice how student m0 (who really scored a zero)
first gets his grade right, but if I invoke it again, then it gets 50.0.

>>> grade_student(m0)
{'grade': 0.0, ...}
>>> grade_student(m0)
{'grade': 50.0, ...}

(*) Where's the problem?

The variable m0 is a module and modules in Python are effectively
singletons --- that is, they are meant to be loaded only once, no matter
how many times you ask the system to.  That's my understanding.

(*) How did I get into this mess?

When m0 is graded first, that is the first student's module ever loaded,
so everything turns out correct.  This module m0 didn't do any work ---
didn't write any procedures, so it gets grade = 0 ---, so when I check
the first procedure, it doesn't exist --- zero on question 1.  However,
question 2 depends on question 1.  So I use the test's key (which I
wrote, which is perfectly correct) and I augment the student's module
with the key's procedures that are prerequisites to question 2 and then
I test question 2.  How do I do that?

  I do m.question1 = key.question1

where ``key.question1'' is a correct procedure for getting all points of
question1.  (That's kind to the student: I'm allowing them to get a zero
on question 1 while perhaps getting question 2 right.)  However, once I
augment the student's code, I can' t find a way to properly restore it
to the original --- so on a second execution, m0 gets many more points.

That's not the whole problem.  For reasons I don't understand, new
modules I load --- that is, different students --- get mixed with these
modifications in m0 that I made at some point in my code.

Of course, you want to see the code.  I need to work on producing a
small example.  Perhaps I will even answer my own question when I do.

For now, let me just ignite your imagination.  Feel free to ignore all
of this and wait for a nice example of the problem.

(*) The code

How do I load a student's file?

--8<---cut here---start->8---
from importlib import *
def get_student_module(fname): 
  # Here fname := p1_hello.py. But we need to remove the extension.
  # Let's call it basename then.
  mod_name = basename(fname)
  try:
student = import_module(mod_name)
  except Exception as e:
return False, str(e)
  return True, student
--8<---cut here---end--->8---

Now let d be a path to a directory.  How do use I this procedure?

--8<---cut here---start->8---
  for f in get_all_tests(d):
okay, student = get_student_module(f)
report = grade_student(student)
[...]
--8<---cut here---end--->8---

What does grade_student(student_module) do?  It passes student_module to
procedures to check every little thing the test requires.  Let's take a
look at question4().

Question 4 requires a previous procedure called procedure_x.  So I
overwrite the student's module with the key's procedure.

def question4(m):
  # We first provide students with all the prerequisites of the
  # question, so they don't necessarily get this one wrong by getting
  # prerequisites wrong.
  m.procedure_x = key.procedure_x
  
  # Now I make all my verifications about, say, m.procedure_y and I
  # compute an integer called losses, which I return.

  return losses

This strategy must be so unwise that it totally breaks my naĂŻve dream of
automatic grading, showing how unfit I am for teaching students how to
program.  Such is life.

(*) If it were easy to unload modules...

I could just unload it and load it again.  That should restore the
student's module back to its original source code.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-16 Thread Hope Rouselle
Grant Edwards  writes:

> On 2021-08-12, Hope Rouselle  wrote:
>
>>> OS/2 had all kinds of amazing features (for its time). [...] Plus,
>>> it had this fancy concept of "extended attributes"; on older
>>> systems (like MS-DOS's "FAT" family), a file might be Read-Only,
>>> Hidden, a System file, or needing to be Archived, and that was it -
>>> but on HPFS, you could attach arbitrary data like "File type:
>>> DeScribe Word Processor" or "Double click action: Run
>>> CASMake.cmd". This allowed the GUI to store all kinds of
>>> information *on the file itself* instead of needing hidden files
>>> (or, in Windows' case, the registry) to track that kind of thing.
>>
>> Yeah, that's kinda nice.  Isn't that a UNIX design?  A file is a
>> sequence of bytes?  Users decide what to put in them?
>
> I think what he's talking about is allowing the user to attach
> arbitrary _metadata_ to the file -- metadata that exists separately
> and independently from the normal data that's just a "sequence of
> bytes". IOW, something similar to the "resource fork" that MacOS used
> to have. https://en.wikipedia.org/wiki/Resource_fork

Got ya.

>> So OS/2 was taking advantage of that to integrate it well with the
>> system.  Windows was doing the same, but integrating the system with
>> files in odd ways --- such as a registry record to inform the system
>> which programs open which files?  (That does sound more messy.)
>
> Windows never had filesystems that supported metadata like OS/2 and
> MacOS did. The registry was an ugly hack that attempted (very poorly)
> to make up for that lack of metadata.

Take a look at NTFS streams.  Maybe you would consider that as a way to
store metadata in Windows.  Pavel Yosifovich talks about them briefly in
chapter 11, page 555, part 1.  Windows 10 System Programming.  Here's
the first paragraph:

--8<---cut here---start->8---
The NTFS filesystem supports /file streams/, which are essentially files
within a file.  Normally, we use the default data stream, but others can
be created and used. These are essentially hidden from normal view and
don’t show up in standard tools such as Windows Explorer.
--8<---cut here---end--->8---

Pavel Yosifovich wrote a tool to let us see these streams:

https://github.com/zodiacon/NtfsStreams

>> UNIX's execve() is able to read the first line of an executable and
>> invoke its interpreter.  I guess OS/2 was doing precisely that in a
>> different way?
>
> No, that's not at all the same thing. The #! line is part of the
> normal file data. It's part of the 'sequence of bytes'. Metadata
> maintained by Unix filesystems comprises a very limited and
> pre-defined set of attributes present in the inode.

Well said.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-16 Thread Hope Rouselle
Hope Rouselle  writes:

[...]

>> Granted you may have to restrict some features if [...]
>
> To let students use the entire language feels a bit weird in the sense
> that the group goes in so many different directions.  It definitely put
> teachers in a position they have to be --- I don't know the answer.
  ^^
Sorry.  I meant ``they [hate] to be [in] --- I don't know the answer.''

And, alas, I also find many more typos, grammar and cosmetic
incoherences below, which I fix with brackets.  (Not that they were
needed.  Only this bit above seemed to lose the meaning of the author,
Your Truly.)

> It is not my case.  But I do think that holding a class with wildly
> different backgrounds, each going about in their own weird ways[,] is
> kinda odd.  It doesn't seem to bring people together --- on average.
>
> The better-idea brings people together by leveling everyone out.
> Unpopular languages give us this feature.  Students hardly ever master
> them.  It's a game nobody played.  The rules are very simple.  The
> implications are far-fetching.  Sometimes even the programmer-expert in
> class realizes he is less skilled than the total-novice that never
> handled a compiler: his tricks don't work in the new game.  (It's not
> that we don't allow him to use them.  They're not there.  They don't
> compile.)  (We take [Chess] players, Backgammon and Checkers players [the
> students], we teach them a new game, say, Go, and suddenly everyone is
> learning together.  Assume Go is unpopular.  It's very simple.  Everyone
> learns the rules quickly and we spend the semester looking into
> strategies.  Much better idea.)
>
>>  For my Algorithm/Data Structure course (circa 1978), the instructor
>> allowed us to use any language /he/ could understand (so no SNOBOL). At the
>> time I'd gone through intro/advanced FORTRAN-4, intro/advanced COBOL, Sigma
>> Assembly, UCSD Pascal (not on the campus main computer, just a pair of
>> LSI-11s), and BASIC. The assignment was a "Hashed Head, Multiply-Linked
>> List". I chose to do that assignment using BASIC! In nearly 45 years, I've
>> only seen ONE real-world implementation of the HHMLL -- The file system
>> used by the Commodore Amiga. (Hash the first component of the path/name,
>> that maps to one of 64 entries in the root directory block; each entry
>> points the start of a linked list, follow the list until you reach the
>> block with the component name; if it is a directory block, hash the next
>> component and repeat; if it is a file block, the "entries" point to data
>> blocks instead of lists)
>
> Repeating my criticism with one more illustration.  When ``there's
> always more than one way to do it'', students can't even count on their
> classmates to help each other --- because each one is doing a different
> thing.  This is good in science, but what I like the most in schooling
> is working together and too much freedom like seems not very helpful in
> this direction.
>
> But both worlds [are] possible.  Use a limited tool (which is not
> computationally limited, quite the contrary) and tell them --- you can
> do anything you want with this.  [...] [Although] everyone is more or
> less original, the different solutions are never too far apart, so all
> the exchange is quite possible.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-16 Thread Hope Rouselle
Chris Angelico  writes:

> On Fri, Aug 13, 2021 at 2:15 AM Hope Rouselle  wrote:
>>
>> Chris Angelico  writes:
>>
>> > History lesson!
>> >
>> > Once upon a time, IBM and Microsoft looked at what Intel was
>> > producing, and went, hey, we need to design an operating system that
>> > can take advantage of the fancy features of this 80286 thing. So they
>> > collaborate on this plan to make a 16-bit protected mode OS.
>> > Unfortunately, things didn't work out too well, partly because this
>> > was when Microsoft was at its most monopolistic, and they ended up
>> > parting company. IBM continued to make OS/2, but Microsoft took their
>> > part of the code and made Windows NT out of it.
>>
>> How is it possible that Microsoft would take part of the code of OS/2?
>> Did IBM just hand it to them?
>
> I presume both companies had all of the code. It was a matter of
> licensing, though. There were a few components that were saddled with
> awkward restrictions due to the dual ownership (for instance, HPFS386
> was Microsoft-controlled, but vanilla HPFS was fine - kinda like the
> difference between LZW and LZ77).
>
>> > (Aside: Windows NT's 16-bit applications and OS/2's 16-bit
>> > applications were actually identical and compatible. Unfortunately,
>> > Win32 introduced a very new API, so as soon as everyone moved to
>> > 32-bit everything, the schism became problematic. But it was actually
>> > possible to package up a single .EXE file with a 16-bit MS-DOS loader,
>> > a Win32 loader, and an OS/2 32-bit loader, all happily coexisting.
>>
>> Beautiful. :-) So if all libraries were around in each system, they had
>> perfect compatibility?
>
> The 16-bit loaders were fine, but the 32-bit loaders were different,
> so this trick basically meant having three different copies of the
> code wrapped up in a single executable.
>
>> > Plus, it had this fancy
>> > concept of "extended attributes"; on older systems (like MS-DOS's
>> > "FAT" family), a file might be Read-Only, Hidden, a System file, or
>> > needing to be Archived, and that was it - but on HPFS, you could
>> > attach arbitrary data like "File type: DeScribe Word Processor" or
>> > "Double click action: Run CASMake.cmd". This allowed the GUI to store
>> > all kinds of information *on the file itself* instead of needing
>> > hidden files (or, in Windows' case, the registry) to track that kind
>> > of thing.
>>
>> Yeah, that's kinda nice.  Isn't that a UNIX design?  A file is a
>> sequence of bytes?  Users decide what to put in them?  So OS/2 was
>> taking advantage of that to integrate it well with the system.  Windows
>> was doing the same, but integrating the system with files in odd ways
>> --- such as a registry record to inform the system which programs open
>> which files?  (That does sound more messy.)
>
> Something like that, but with a lot more metadata. Modern OSes don't
> seem to work that way any more.
>
>> UNIX's execve() is able to read the first line of an executable and
>> invoke its interpreter.  I guess OS/2 was doing precisely that in a
>> different way?
>
> Kinda, but instead of having the choice of interpreter be inside the
> file contents itself, the choice was in the file's metadata. Still
> part of the file, but if you open and read the file, it isn't any
> different.

Speaking of which, NTFS supports something called file streams.  That's
essentially a way to have files inside files.  Streams are not shown by
tools such as file explorer.  Applications could definitely use that as
metadata.  (End of tangent.)

I think UNIX people would say and I'd agree --- we have that already.
But instead of hiding the stream, which is obscurantism, we let the user
see.  (Just create two files in a directory, one with the extension
.metadata.  And, oh, if you don't like that extension, you can change it
to whatever you like.  Lol.)  I like that.

>> > The default command interpreter and shell on OS/2 was fairly primitive
>> > by today's standards, and was highly compatible with the MS-DOS one,
>> > but it also had the ability to run REXX scripts. REXX was *way* ahead
>> > of its time. It's a shell language but remarkably well suited to
>> > building GUIs and other tools (seriously, can you imagine designing a
>> > GUI entirely in a bash script??).
>>
>> I cannot imagine.  I always wondered what REXX was about --- I saw
>> programs sometimes written in some website whose name is something like
>> Rosetta Code.  REXX looked so weird.  (``Wh

Re: some problems for an introductory python test

2021-08-16 Thread Hope Rouselle
Dennis Lee Bieber  writes:

> On Wed, 11 Aug 2021 09:27:38 -0300, Hope Rouselle 
> declaimed the following:
>>
>>I wouldn't.  This is all Python-stuff.  The course chooses a language
>>like Python, but it is not trying to teach Python --- it is trying to
>>teach computer programming, that is, strategies in high-precision.
>
>   Then I would posit you are teaching the wrong level... What you
> describe is what would be found a "data structures" and/or "algorithms"
> course -- which is a second year, if not third year course in my (ancient)
> history. [...] Slicing with a stride isn't really useful to most
> algorithm development -- it's more of trick in normal Python.

I didn't express myself very well.  And it's hard to make sense of the
course.  I say it's interested in teaching computer programming because
it is not allowing the students to dive into the Python language.  It's
very restrictive.  Students will finish the course after learning to use
the loop contructs while and for.  The course calls them ``structures
for repetition''.  I think it's safe to say that the course is designed
by people concerned with education, but not well-versed in programming
languages, so not even the terminology seems quite correct.

So if they're not letting students embrace the whole of the language,
they must be concerned with other things?  They sloppily emphasize
documentation and put in some concerns for procedure signatures.  I have
some trouble defending it.  I see the good intentions, though, and the
job is not easy.

But this course is definitely not teaching any data structures or any
algorithms.  Zero.  Students are using tuples, lists and dictionaries,
strings and numbers, but they're not studying any strategies whatsoever.
The exercises make students use a few basic facts of mathematics, but
there is pretty much no investigation into the strategies behind some of
these facts.  It's not very intelligible.

I *kinda* like that it's restrictive because it allows us to show
students at least a few idioms: otherwise there's so much syntax to
digest that there is no time to show a few useful applications of them.
But then I read your next paragraph and it brings me to a very important
point, which I think it's at the back of your mind too.  Let's see.

>   So... The first thing I would drop is the RESTRICTION to only use what
> you've covered in prior sessions... Let the students read the language
> reference manual, peruse the library reference manual, and allow them to
> use whatever "advanced" techniques they are able to understand on their
> own. 

Precisely.  We kill the spirit of investigation by saying --- don't do
anything I don't allow you to.  This is pretty dangerous.  I have a much
better idea, I think.

You have to give people a tool and say --- use however you want.  If we
don't want them to use things not introduced in previous lessons, we
should give them a precisely-limited tool.  But that's not done.

The much better idea is to use such a limited language.  There are such
languages, made precisely for teaching.  It comes with very little
syntax and lesson after lesson it gets immensely powerful with very
little syntax.

> Granted you may have to restrict some features if [...]

To let students use the entire language feels a bit weird in the sense
that the group goes in so many different directions.  It definitely put
teachers in a position they have to be --- I don't know the answer.  It
is not my case.  But I do think that holding a class with wildly
different backgrounds, each going about in their own weird ways is kinda
odd.  It doesn't seem to bring people together --- on average.

The better-idea brings people together by leveling everyone out.
Unpopular languages give us this feature.  Students hardly ever master
them.  It's a game nobody played.  The rules are very simple.  The
implications are far-fetching.  Sometimes even the programmer-expert in
class realizes he is less skilled than the total-novice that never
handled a compiler: his tricks don't work in the new game.  (It's not
that we don't allow him to use them.  They're not there.  They don't
compile.)  (We take Ghess players, Backgammon and Checkers players [the
students], we teach them a new game, say, Go, and suddenly everyone is
learning together.  Assume Go is unpopular.  It's very simple.  Everyone
learns the rules quickly and we spend the semester looking into
strategies.  Much better idea.)

>   For my Algorithm/Data Structure course (circa 1978), the instructor
> allowed us to use any language /he/ could understand (so no SNOBOL). At the
> time I'd gone through intro/advanced FORTRAN-4, intro/advanced COBOL, Sigma
> Assembly, UCSD Pascal (not on the campus main computer, just a pair of
> LSI-11s), and BASIC. The assignment was a "Hashed Head, Multiply-Linked
> List". I chose to do tha

Re: some problems for an introductory python test

2021-08-16 Thread Hope Rouselle
Chris Angelico  writes:

> On Thu, Aug 12, 2021 at 9:23 AM Dennis Lee Bieber  
> wrote:

[...]

>> I was spoiled by the Amiga variant of REXX. Most current
>> implementations (well, Regina is the only one I've looked at) can just pass
>> command to the default shell. The Amiga version took advantage of Intuition
>> Message Ports (OS supported IPC). That allowed it to "address
>> " any application that defined an ARexx port, allowing ARexx
>> to be used as a scripting language for that application (and with multiple
>> applications, one could easily fetch data from app1 and feed it to app2).
>> ARexx did not, to my memory, implement arbitrary precision math.
>
> The same functionality was available in OS/2, but not heavily used.
> You could 'address cmd commandname' to force something to be
> interpreted as a shell command, but that was about it. However, I
> built a MUD that used REXX as its scripting language, and the default
> destination was sending text back to the person who sent the command;
> and you could, of course, still 'address cmd' to run a shell command.
>
>> I've not seen anything equivalent in my light perusal of the Win32 
>> API
>> (the various guide books aren't layed out in any way to be a reference),
>> and Linux seems to use UNIX sockets for IPC... No way to search for a
>> connection point by name...
>
> Win32 doesn't really have it. Unix sockets are kinda there but you
> identify something by a path to the socket, not the name of the
> application. But I think dbus is probably the closest to what you're
> thinking of.

Win32 offers some APIs that sound like it could be what you guys are
talking about here that I'm not too clear what it is.  There are some
shared memory you can reference by name, so it's as easy to use: both
applications invoke a procedure with a string-argument and exchange
information that way.  (And Win32 does support named pipes as well.)  I
mean I'm sure you know it all better than I do.  (Perhaps I'm too
clueless here.)  I could look up the Win32 APIs I'm thinking of if that
would clarify things.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-16 Thread Hope Rouselle
Dennis Lee Bieber  writes:

> On Thu, 12 Aug 2021 06:15:28 +1000, Chris Angelico 
> declaimed the following:
>
>>The default command interpreter and shell on OS/2 was fairly primitive
>>by today's standards, and was highly compatible with the MS-DOS one,
>>but it also had the ability to run REXX scripts. REXX was *way* ahead
>>of its time. It's a shell language but remarkably well suited to
>>building GUIs and other tools (seriously, can you imagine designing a
>>GUI entirely in a bash script??). It had features that we'd consider
>>fairly normal or even primitive by Python's standards, but back then,
>>Python was extremely new and didn't really have very much mindshare.
>>REXX offered arbitrary-precision arithmetic, good databasing support,
>>a solid C API that made it easy to extend, integrations with a variety
>>of other systems... this was good stuff for its day. (REXX is still
>>around, but these days, I'd rather use Python.)
>>
>   I was spoiled by the Amiga variant of REXX. Most current
> implementations (well, Regina is the only one I've looked at) can just pass
> command to the default shell. The Amiga version took advantage of Intuition
> Message Ports (OS supported IPC). That allowed it to "address
> " any application that defined an ARexx port, allowing ARexx
> to be used as a scripting language for that application (and with multiple
> applications, one could easily fetch data from app1 and feed it to app2).
> ARexx did not, to my memory, implement arbitrary precision math.
>
>   Any statement in a REXX script that was not parsable as REXX would be
> passed on the currently "addressed" command host.
>
>   Granted, the fact that the Amiga used a shared common address space for
> all running applications made IPC quite easy -- one looked up the
> application message port, then added a small packet to the linked list
> associated with the port. That small packet basically held the address of
> the message port for returning data, and the address of the data being
> passed. The closet thing I've seen to that capability on systems with
> process-isolated virtual memory is (Open)VMS "mailbox" structures. The
> difference being that the entire data had to be written (QIO) to the
> mailbox, and the receiver had to read (another QIO call) the message --
> this allowed the address space to change.
>
>   I've not seen anything equivalent in my light perusal of the Win32 API
> (the various guide books aren't layed out in any way to be a reference),
> and Linux seems to use UNIX sockets for IPC... No way to search for a
> connection point by name...

I don't know anything about Amiga, REXX et cetera, so I might be totall
off here. But since you spoke of your perusal of the Win32 API, let me
add a tiny bit.  I gave myself a quick tour through the Win32 API using
Pavel Yosifovich's book ``Windows 10 System Programming''.  It's a two
volume work.  The thing that impressed me the most was the many ways to
do the IPC.  The purpose the work is clearly to show what is available
and it it probably does the job well.  (I ignored Windows for most of my
life and now I decided to take a look at it.  I don't feel it has much
of the elegance of UNIX.  It's what it is.)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-12 Thread Hope Rouselle
Chris Angelico  writes:

[...]

>> > [1] And boy oh boy was that good fun. The OS/2 Presentation Manager
>> > had a wealth of power available. Good times, sad that's history now.
>>
>> I know OS/2 only by name.  I never had the pleasure of using it.  In
>> fact, I don't even know how it looks.  I must be a little younger than
>> you are.  But not too younger because I kinda remember its name.  Was it
>> a system that might have thought of competing against Microsoft Windows?
>> :-) That's what my memory tells me about it.
>
> History lesson!
>
> Once upon a time, IBM and Microsoft looked at what Intel was
> producing, and went, hey, we need to design an operating system that
> can take advantage of the fancy features of this 80286 thing. So they
> collaborate on this plan to make a 16-bit protected mode OS.
> Unfortunately, things didn't work out too well, partly because this
> was when Microsoft was at its most monopolistic, and they ended up
> parting company. IBM continued to make OS/2, but Microsoft took their
> part of the code and made Windows NT out of it.

How is it possible that Microsoft would take part of the code of OS/2?
Did IBM just hand it to them?

> (Aside: Windows NT's 16-bit applications and OS/2's 16-bit
> applications were actually identical and compatible. Unfortunately,
> Win32 introduced a very new API, so as soon as everyone moved to
> 32-bit everything, the schism became problematic. But it was actually
> possible to package up a single .EXE file with a 16-bit MS-DOS loader,
> a Win32 loader, and an OS/2 32-bit loader, all happily coexisting.

Beautiful. :-) So if all libraries were around in each system, they had
perfect compatibility?

> In the latter half of the 1990s, Windows came in three broad flavours:
> the 16-bit Windows 3.x line, which ran purely on top of DOS; the
> 32-bit Windows 95 line, which was the "home" OS and had to be heavily
> compatible with Windows 3, but still doing more things; and the
> Windows NT line, which was aimed at businesses. OS/2 was a viable
> option for businesses, but almost nobody took it seriously as a home
> OS. And Microsoft was using its marketing machine to push Windows
> fairly hard, so most people went that way.
>
> OS/2 had all kinds of amazing features (for its time). The default
> file system, HPFS ("High Performance File System"... IBM was good at
> many things, but imaginative naming wasn't one of them), did a
> spectacular job of maintaining reliability and performance on the
> ever-growing hard disks of the time. Yes, it coped remarkably well,
> even on *gigantic* drives that could carry, ooh, an entire gigabyte of
> data! I'm not kidding! We even had a drive that had TWO gigabytes of
> space, and HPFS managed it beautifully! Plus, it had this fancy
> concept of "extended attributes"; on older systems (like MS-DOS's
> "FAT" family), a file might be Read-Only, Hidden, a System file, or
> needing to be Archived, and that was it - but on HPFS, you could
> attach arbitrary data like "File type: DeScribe Word Processor" or
> "Double click action: Run CASMake.cmd". This allowed the GUI to store
> all kinds of information *on the file itself* instead of needing
> hidden files (or, in Windows' case, the registry) to track that kind
> of thing.

Yeah, that's kinda nice.  Isn't that a UNIX design?  A file is a
sequence of bytes?  Users decide what to put in them?  So OS/2 was
taking advantage of that to integrate it well with the system.  Windows
was doing the same, but integrating the system with files in odd ways
--- such as a registry record to inform the system which programs open
which files?  (That does sound more messy.)

UNIX's execve() is able to read the first line of an executable and
invoke its interpreter.  I guess OS/2 was doing precisely that in a
different way?

> The default command interpreter and shell on OS/2 was fairly primitive
> by today's standards, and was highly compatible with the MS-DOS one,
> but it also had the ability to run REXX scripts. REXX was *way* ahead
> of its time. It's a shell language but remarkably well suited to
> building GUIs and other tools (seriously, can you imagine designing a
> GUI entirely in a bash script??). 

I cannot imagine.  I always wondered what REXX was about --- I saw
programs sometimes written in some website whose name is something like
Rosetta Code.  REXX looked so weird.  (``Who would program in that?'')
But I see now there is a context to it.

> It had features that we'd consider fairly normal or even primitive by
> Python's standards, but back then, Python was extremely new and didn't
> really have very much mindshare.  REXX offered arbitrary-precision
> arithmetic, good databasing support, a solid C API that made it easy
> to extend, integrations with a variety of other systems... this was
> good stuff for its day. (REXX is still around, but these days, I'd
> rather use Python.)

Yeah, REXX looks horrible at first.  But arbitrary-precision is
definitely very attractive. 

Re: some problems for an introductory python test

2021-08-11 Thread Hope Rouselle
Chris Angelico  writes:

> On Wed, Aug 11, 2021 at 4:18 AM Hope Rouselle  wrote:
>>
>> Chris Angelico  writes:
>>
>> [...]
>>
>> >> not disagreeing... and yeah I could have thought deeper about the
>> >> answer, but I still think "notthing has been OOP" -> "yes it has, they
>> >> just didn't realize it"  was worth mentioning
>> >
>> > Oh yes, absolutely agree.
>>
>> At the same time, inside the machine nothing is OOP --- so all the OOP
>> is no OOP at all and they just didn't realize it?  This seems to show
>> that OOP is about perspective.  An essential thing for OOP is the
>> keeping of states.  Closures can keep state, so having procedures as
>> first-class values allows us to say we are doing OOP too.  (Arguments of
>> procedures are messages and function application is message passing,
>> with closures keeping a state --- and all the rest of OOP can be
>> implemented with enough such functional technology.)  In summary, OOP
>> should not be defined as some special syntax, otherwise there is no OOP
>> in ``2 + 2''.
>>
>> Having said that, I totally agree with all the nitpicking.
>
> Object orientation is a particular abstraction concept. It's not a
> feature of the machine, it's a feature of the language that you write
> your source code in. I've done OOP using IDL and CORBA, writing my
> code in C and able to subclass someone else's code that might have
> been written in some other language. [1] Central tenets of OOP
> (polymorphism, inheritance, etc) can be implemented at a lower level
> using whatever makes sense, but *at the level that you're writing*,
> they exist, and are useful.
>
> Data types, variables, control flow, these are all abstractions. But
> they're such useful abstractions that we prefer to think that way in
> our code. So, *to us*, those are features of our code. To the
> computer, of course, they're just text that gets processed into
> actually-executable code, but that's not a problem.

Total agreement.

> So I would say that (in Python) there IS object orientation in "2 +
> 2", and even in the Python C API, there is object orientation, despite
> C not normally being considered an object-oriented language.

But would you say that 2 + 2 is also an illustration of object
orientation in any other language too? 

Regarding C, I have many times said that myself.  If I wrote assembly,
I'm sure I would do my best to create things like procedures --- a label
and some bureaucracy to get arguments in and a return value out.

> ChrisA
>
> [1] And boy oh boy was that good fun. The OS/2 Presentation Manager
> had a wealth of power available. Good times, sad that's history now.

I know OS/2 only by name.  I never had the pleasure of using it.  In
fact, I don't even know how it looks.  I must be a little younger than
you are.  But not too younger because I kinda remember its name.  Was it
a system that might have thought of competing against Microsoft Windows?
:-) That's what my memory tells me about it.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-11 Thread Hope Rouselle
Chris Angelico  writes:

> On Wed, Aug 11, 2021 at 4:18 AM Hope Rouselle  wrote:
>>
>> I totally agree with you but I didn't know that even numbers were like
>> that in Python.  In fact, I still don't quite believe it...
>>
>> >>> 2.__add__(3)
>> SyntaxError: invalid syntax
>
> Yeah, that's because "2." looks like the beginning of a float.
>
>> But then I tried:
>>
>> >>> (2).__add__(3)
>> 5
>>
>> Now I do believe it! :-)  Awesome.  I had no idea.
>
> You can also do it this way:
>
>>>> x = 2
>>>> x.__add__(3)
> 5
>
> But don't teach this; it ignores several problems. 

I wouldn't.  This is all Python-stuff.  The course chooses a language
like Python, but it is not trying to teach Python --- it is trying to
teach computer programming, that is, strategies in high-precision.

[...]

>> (*) More opinions
>>
>> So, yeah, the idea of a course like that is to try to simplify the world
>> to students, but it totally backfires in my opinion.  There is so much
>> syntax to learn, so many little details...  We spend the entire semester
>> discussing these little details.
>
> Agreed, and if you try to teach all the syntax, you're inevitably
> going to get bogged down like that.

Indeed.

>> I posted here recently a study of the semantics of slices.  When I
>> finally got it, I concluded it's not very simple.  The course introduces
>> a few examples and expects students to get it all from these examples.
>> I would rather not introduce slices but teach students enough to write
>> procedures that give them the slices.  The slices are the fish; learning
>> to write the procedures is the know-how.  (I'm fine with even teaching
>> them how to write procedures to add or subtract numbers [and the rest of
>> arithmetic], although none of them would find mysterious what is the
>> meaning of arithmetic expressions such as 3 + 6 - 9, even taking
>> precedence of operators into account.  That's syntax they already know.
>> If we teach them the syntax of procedures, we could be essentially done
>> with syntax.)
>
> A language like Python is useful, not because every tiny part of it is
> easy to explain, but because the language *as a whole* does what is
> expected of it. Toy languages are a lot easier to explain on a
> concrete level (look up Brainf*ck - it has very few operations and
> each one can be very simply defined), and that might be tempting in
> terms of "hey, we can teach the entire language in ten minutes", but
> they're utterly useless for students.

Sure.  I would never use a toy language like that.  I think we need a
compromise --- some syntax, but not too much; in fact, just a little bit
of syntax.  For instance, could we do away with slices?  We could.  If
we learn one way of looping, we can build slices with procedures.  This
is a good opportunity in fact --- it will show students how slices
really work and how to get what they want with it.  (Then towards the
end we could give out a lecture for anyone interested in Python ---
``while we didn't show you how Pythonists usually write, here's a few
things that Pythonists would do that we didn't do in this course...'')

> Instead, my recommendation would be: Entice students with power.
> Pretend you're teaching some aspect of magic at a wizards' school and
> make sure your students feel massively OP. Sure, they might not
> understand what makes those cantrips work, but they know that they DO
> work. You can have students casting print calls left and right, doing
> complex incantations with list comprehensions, and even defining their
> own classes, all without ever worrying about the details of how it all
> works. Then once the "wow" is achieved, you can delve into the details
> of how things actually function, transforming progressively from
> "wizardry" to "science".

I really like that, but I would predict that, in a typical university,
professors are not really willing to go that way.  Everything has to be
extremely simple --- and a mess is made in trying to achive that.  (In a
sense, professors are all failing terribly.  They're lucky that nobody
is giving them a grade based on how effective they are as evidenced by
the facts, however such thing would be done in practice if one were to
take it seriously.)

> And to be quite frank, a lot of code IS magic to most programmers.

Definitely.  You guys are showing me so much Python here that I've never
seen --- which I expect, because I'm only paying attention at Python now
since I'm conducting students through a course that's using it.  (I
admire the language, but I never used it in any project on my own.  I
probably would if I were not in love with

Re: some problems for an introductory python test

2021-08-11 Thread Hope Rouselle
Greg Ewing  writes:

> On 11/08/21 3:22 pm, Terry Reedy wrote:
>> Python is a little looser about whitespace than one might expect
>> from reading 'normal' code when the result is unambiguous in that it
>> cannot really mean anything other than what it does.
>>  >>> if3: print('yes!')
>> yes!
>
> That may not be doing what you think it's doing. Consider also
>
 if0: print('yes!')
> yes!

So, yes, that's puzzling.

>>> 0 == False
True
>>> if0: print("yes")
yes
>>> if(0): print("yes")

>>>

What's going on there?
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-11 Thread Hope Rouselle
Terry Reedy  writes:

> On 8/10/2021 5:27 PM, Hope Rouselle wrote:
>> Terry Reedy  writes:
>> 
>>> On 8/10/2021 9:15 AM, Hope Rouselle wrote:
>>>>>>> 2.__add__(3)
>>>> SyntaxError: invalid syntax
>>>> But then I tried:
>>>>
>>>>>>> (2).__add__(3)
>>>> 5
>>>
>>> Add a space is easier.
>>>>>> 2 .__add__(3)
>>> 5
>>>>>>
>> Hah.  That's brilliant!  So cool.
>
> Python is a little looser about whitespace than one might expect from
> reading 'normal' code when the result is unambiguous in that it cannot 
> really mean anything other than what it does.  Two other examples:
>
>>>> if3: print('yes!')
> yes!

That's cool to know too, but I would have expected that.  Programming
languages tend to ignore whitespace as much as possible.  (But someone
followed-up with more details...  I'll get there.)  But the next one...

>>>> [0]  [0]
> 0

Oh!  This almost fooled me.  It's just a list containing the integer
zero followed by the index-operator (or however the brackets are called
in this case.)

At first I thought maybe there was an implicit operator between lists
just like it happens with strings.

>>> "a" "," "b"
'a,b'

But which operator would it be?  It wasn't concatenation, of course.  So
I looked at what type() thought of it.  Then I tried changing numbers:

>>> [1] [1]
Traceback (most recent call last):
  File "", line 1, in 
[1] [1]
IndexError: list index out of range

And that's when it hit me. :-)  Thanks!
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-10 Thread Hope Rouselle
Terry Reedy  writes:

> On 8/10/2021 9:15 AM, Hope Rouselle wrote:
>>>>> 2.__add__(3)
>> SyntaxError: invalid syntax
>> But then I tried:
>> 
>>>>> (2).__add__(3)
>> 5
>
> Add a space is easier.
>>>> 2 .__add__(3)
> 5
>>>>

Hah.  That's brilliant!  So cool.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-10 Thread Hope Rouselle
Chris Angelico  writes:

> On Wed, Aug 11, 2021 at 4:14 AM Hope Rouselle  wrote:
>>
>> Chris Angelico  writes:
>>
>> > On Tue, Aug 10, 2021 at 7:25 AM Hope Rouselle
>> >  wrote:
>> >> I came up with the following question.  Using strings of length 5
>> >> (always), write a procedure histogram(s) that consumes a string and
>> >> produces a dictionary whose keys are each substrings (of the string) of
>> >> length 1 and their corresponding values are the number of times each
>> >> such substrings appear.  For example, histogram("a") = {"a": 5}.
>> >> Students can "loop through" the string by writing out s[0], s[1], s[2],
>> >> s[3], s[4].
>> >
>> > In other words, recreate collections.Counter? Seems decent, but you'll
>> > need to decide whether you want them to use defaultdict, use
>> > __missing__, or do it all manually.
>>
>> Yes, the course introduces very little so there is a lot of recreation
>> going on.  Hm, I don't know defaultdict and I don't know how to use
>> __missing__.  The course does introduce dict.get(), though.  If students
>> use dict.get(), then the procedure could essentially be:
>>
>> def histogram(s):
>>   d = {}
>>   d[s[0]] = d.get(s[0], 0) + 1
>>   d[s[1]] = d.get(s[1], 0) + 1
>>   d[s[2]] = d.get(s[2], 0) + 1
>>   d[s[3]] = d.get(s[3], 0) + 1
>>   d[s[4]] = d.get(s[4], 0) + 1
>>   return d
>
> There's nothing wrong with getting students to recreate things, but
> there are so many different levels on which you could do this, which
> will leave your more advanced students wondering what's legal. :) Here
> are several ways to do the same thing:

[... very impressive set of solutions...]

> It seems *very* strange to have an exercise like this without looping.

It is.  I agree.

> That seems counterproductive. 

It is.

> But if you're expecting them to not use loops, you'll want to also be
> very clear about what other features they're allowed to use - or
> alternatively, stipulate what they ARE allowed to use, eg "Use only
> indexing and the get() method".

Yes, I will do that.  I mean the course does that all the time.  They
cannot use anything that has not been introduced.  That's another
problem because the course introduces various things and students can't
quite keep everything in mind.  The suggestion to make up a list of
things is mostly ignored by nearly all of them. :-)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-10 Thread Hope Rouselle
Mats Wichmann  writes:

> On 8/9/21 3:07 PM, Hope Rouselle wrote:
>> I'm looking for questions to put on a test for students who never had
>> any experience with programming, but have learned to use Python's
>> procedures, default arguments, if-else, strings, tuples, lists and
>> dictionaries.  (There's no OOP at all in this course.  Students don't
>> even write ls.append(...).  They write list.append(ls, ...)).
>
> Nitpickery... there *is* OOP in the course, they just don't know it.
>
> Long long ago (over 20 yrs now) I developed a Python course for a
> commercial training provider, and in it I claimed one of the great 
> things about Python was it supported all kinds of object oriented
> programming techniques, but you could also use it without doing
> anything object oriented. If I wrote a course now, I'd never make that
> claim, because everything you do in Python is pretty much object
> oriented.
>
>>>> x = list()
>>>> type(x)
> 
>>>> dir(x)
> ['__add__', '__class__', '__class_getitem__', '__contains__',
> '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', 
> '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__',
> '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', 
> '__iter__', '__le__', '__len__', '
> __lt__', '__mul__', '__ne__', '__new__', '__reduce__',
> '__reduce_ex__', '__repr__', '__reversed__', '__rmul__',
> '__setattr__', '__setitem__', '__sizeof__', '__str__',
> '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend',
> 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>
> list is a class and it has methods... it's "object-oriented"!
>
>
> Even if you do
>
> x = 2 + 3
>
> you're actually creating an integer object with a value of 2, and
> calling its add method to add the integer object with the value of 3
> to it. The syntax hides it, but in a way it's just convenience that it
> does so...
>
>>>> 2 + 3
> 5
>>>> x = 2
>>>> x.__add__(3)
> 5
>
>
> sorry for nitpicking :)  But... don't be afraid of letting them know
> it's OOP, and it''s not huge and complex and scary!

I totally agree with you but I didn't know that even numbers were like
that in Python.  In fact, I still don't quite believe it...

>>> 2.__add__(3)
SyntaxError: invalid syntax

But then I tried:

>>> (2).__add__(3)
5

Now I do believe it! :-)  Awesome.  I had no idea.

(*) More opinions

So, yeah, the idea of a course like that is to try to simplify the world
to students, but it totally backfires in my opinion.  There is so much
syntax to learn, so many little details...  We spend the entire semester
discussing these little details.

I posted here recently a study of the semantics of slices.  When I
finally got it, I concluded it's not very simple.  The course introduces
a few examples and expects students to get it all from these examples.
I would rather not introduce slices but teach students enough to write
procedures that give them the slices.  The slices are the fish; learning
to write the procedures is the know-how.  (I'm fine with even teaching
them how to write procedures to add or subtract numbers [and the rest of
arithmetic], although none of them would find mysterious what is the
meaning of arithmetic expressions such as 3 + 6 - 9, even taking
precedence of operators into account.  That's syntax they already know.
If we teach them the syntax of procedures, we could be essentially done
with syntax.)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-10 Thread Hope Rouselle
Chris Angelico  writes:

[...]

>> not disagreeing... and yeah I could have thought deeper about the
>> answer, but I still think "notthing has been OOP" -> "yes it has, they
>> just didn't realize it"  was worth mentioning
>
> Oh yes, absolutely agree.

At the same time, inside the machine nothing is OOP --- so all the OOP
is no OOP at all and they just didn't realize it?  This seems to show
that OOP is about perspective.  An essential thing for OOP is the
keeping of states.  Closures can keep state, so having procedures as
first-class values allows us to say we are doing OOP too.  (Arguments of
procedures are messages and function application is message passing,
with closures keeping a state --- and all the rest of OOP can be
implemented with enough such functional technology.)  In summary, OOP
should not be defined as some special syntax, otherwise there is no OOP
in ``2 + 2''.

Having said that, I totally agree with all the nitpicking.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: some problems for an introductory python test

2021-08-10 Thread Hope Rouselle
Chris Angelico  writes:

> On Tue, Aug 10, 2021 at 7:25 AM Hope Rouselle  wrote:
>> I came up with the following question.  Using strings of length 5
>> (always), write a procedure histogram(s) that consumes a string and
>> produces a dictionary whose keys are each substrings (of the string) of
>> length 1 and their corresponding values are the number of times each
>> such substrings appear.  For example, histogram("a") = {"a": 5}.
>> Students can "loop through" the string by writing out s[0], s[1], s[2],
>> s[3], s[4].
>
> In other words, recreate collections.Counter? Seems decent, but you'll
> need to decide whether you want them to use defaultdict, use
> __missing__, or do it all manually.

Yes, the course introduces very little so there is a lot of recreation
going on.  Hm, I don't know defaultdict and I don't know how to use
__missing__.  The course does introduce dict.get(), though.  If students
use dict.get(), then the procedure could essentially be:
  
def histogram(s):
  d = {}
  d[s[0]] = d.get(s[0], 0) + 1
  d[s[1]] = d.get(s[1], 0) + 1
  d[s[2]] = d.get(s[2], 0) + 1
  d[s[3]] = d.get(s[3], 0) + 1
  d[s[4]] = d.get(s[4], 0) + 1
  return d

>> I think you get the idea.  I hope you can provide me with creativity.  I
>> have been looking at books, but every one I look at they introduce loops
>> very quickly and off they go.  Thank you!
>
> Probably because loops are kinda important? :)

Totally important.  But each course author thinks they know better.
Sometimes a college professor can do very little to help her students.
I am actually fond of functional programming as a first course using a
language with as little syntax as possible.  Python is very nice but
it's not a small language.  It's easy to see courses spending an entire
semester on introducing syntax and this one is no different.  I think
it's more interesting to see all the syntax in a few minutes and spend
the semester on strategies.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: on slices, negative indices, which are the equivalent procedures?

2021-08-10 Thread Hope Rouselle
Chris Angelico  writes:

> On Tue, Aug 10, 2021 at 7:24 AM Jack Brandom  wrote:
>>
>> Greg Ewing  writes:
>>
>> > On 6/08/21 12:00 pm, Jack Brandom wrote:
>> >> It seems
>> >> that I'd begin at position 3 (that's "k" which I save somewhere), then I
>> >> subtract 1 from 3, getting 2 (that's "c", which I save somewhere), then
>> >> I subtract 1 from 2, getting 1 (that's "a", ...), then I subtract 1 from
>> >> 1, getting 0 (that's J, ...), so I got "kcaJ" but my counter is 0 not
>> >> -13, which was my stopping point.
>> >
>> > You need to first replace any negative or missing indices with
>> > equivalent indices measured from the start of the string.
>> >
>> > When you do that in this example, you end up iterating backwards from 3
>> > and stopping at -1.
>>
>> Yeah, that makes sense now.  But it sucks that the rule for replacing
>> negative indices is sometimes missing index and sometimes positive
>> index.  (That is, we can't always use positive indices.  Sometimes we
>> must use no index at all.  I mean that's how it looks to my eyes.)
>
> Sometimes, the index  you need to use is the value None. You cannot
> use a positive number to indicate the position to the left of zero -
> at least, not if you consider numbers to be on a number line.

True --- I understand that now.  (I think someone else in this thread
had pointed that out too. But thanks for clarifying it.)
-- 
https://mail.python.org/mailman/listinfo/python-list


some problems for an introductory python test

2021-08-09 Thread Hope Rouselle
I'm looking for questions to put on a test for students who never had
any experience with programming, but have learned to use Python's
procedures, default arguments, if-else, strings, tuples, lists and
dictionaries.  (There's no OOP at all in this course.  Students don't
even write ls.append(...).  They write list.append(ls, ...)).

I'd like to put questions that they would have to write procedures that
would would be real-world type of stuff, without error checking,
exceptions and such.  So if you think of something more or less cool
that uses loops, we can sometimes simplify it by assuming the input has
a certain fixed size.

I came up with the following question.  Using strings of length 5
(always), write a procedure histogram(s) that consumes a string and
produces a dictionary whose keys are each substrings (of the string) of
length 1 and their corresponding values are the number of times each
such substrings appear.  For example, histogram("a") = {"a": 5}.
Students can "loop through" the string by writing out s[0], s[1], s[2],
s[3], s[4].

I'd like even better questions.  I'd like questions that would tell them
to write procedures that would also have inverses, so that one could
check the other of the other.  (A second question would ask for the
inverse, but hopefully real world stuff.  One such question could be
parsing a line separate by fields such as "root:0:0:mypass:Super User"
and another that gives them ["root", 0, 0, ...] and asks them to write
"root:0:0:mypass:..."  You get the idea.)

Students know how to use str().  But they don't know how to use type(),
so they can't really check for the type of the input.  I probably
couldn't ask them to write a prototype of a tiny subset of pickle, say.

I think you get the idea.  I hope you can provide me with creativity.  I
have been looking at books, but every one I look at they introduce loops
very quickly and off they go.  Thank you!
-- 
https://mail.python.org/mailman/listinfo/python-list