Emulating Pascal input

Hi, We're currently running a trial implementation of a teaching course in Python here at the University of Oxford Physics department. The current course uses Pascal and, although it works well, is obviously sub-optimal for a modern undergraduate education. Details of the results of the trial will be posted to EDU-sig when it is complete, but for now we're trying to address what we see as a limitation of Python for introductory programming, namely input. Consider the following Pascal code: (******** Pascal code **********) (* Pascal ascii input of numbers: stdin and file *) readln(x, y); readln(fin, x, y); The first line is for stdin, the second for from file. The variables x and y will then contain the first two white space separated numbers the program encounters when that function is called. Here's how we're currently getting students to do the equivalent in Python: ######### Python code ########### # Either: stdin linestring = raw_input() # Or: file linestring = f.readline() linelist = string.split(linestring) x = int(linelist[0]) y = int(linelist[1]) # Or perhaps linestring = raw_input() x,y = string.split(linestring) # x and y are now strings eg. '1' # and '2' x = int(x) y = int(y) Having read through the many threads on this topic on comp.lang.python I realise and understand that modern programs' input generally requires thought and customization from the program author. However, we strongly feel there should be an equivalent to Pascal's readln (which is basically C's scanf but without the formatting strings) for simple, generally numerical, whitespace-separated input. In the two weeks out trial has been running (one to go), students' attempts to read in simple two column tables from a file has consistently caused problems due to the (relative!) complexity of string splitting. However, There is no need for a scanf (if your input is sufficiently complex to require formatting strings then either string or regex splitting is definitely a more elegant solution). I have been attempting to write such a function in Python but have been struggling with (a) the lack of pointers, and (b) my total ignoarnce of classes, which I suspect would be helpful. Here's what I have so far (although note there is no error handling--give it a string response and it falls over and dies, give it the wrong number of responses and it falls over and dies, etc., etc.): ############ ninput.py ############ import string def ninput(n, fin = None, prompt = '', sep = None): # If not file input then get string from stdin, else get from file if fin == None: linestring = raw_input(prompt) else: linestring = fin.readline() # Split up into list with optional seperator argument responses = string.split(linestring, sep) for i in range(len(responses)): if '.' in responses[i] or 'e' in responses[i] or \ 'E' in responses[i]: # i.e. if it looks like a float responses[i] = float(responses[i]) else: # i.e if it doesn't look like a float responses[i] = int(responses[i]) # If there was only one response then don't return a list. if len(responses) == 1: responses = responses[0] return responses[:n] #################################### And here is a usage example:
x, y = ninput(2) 5 6.0 print x, type(x) 5 <type 'int'> print y, type(y) 6.0 <type 'float'>
This has the limitation that the user must pass the number of results they expect back. This is done implicitly in the Pascal version too (the number of variables passed to the function). However, error checking is difficult as this version stands. If the student using the ninput function does, e.g.
x = ninput(2) 5 6.0 [5, 6.0] <type 'list'>
This is possibly what should be expected. However, passing the numerical argument representing the number of results expected does not strike me as a particularly graceful solution. We would prefer something like the Pascal way, where the global variables in which the results are to be stored are passed as parameters. Can anyone suggest a way of doing this? And if such an equivalent to the Pascal construct was implemented, what would be the chances of getting it included in, say, the string module of core Python. We would like such a function but would be reluctant to use it as we would then be teaching our students a ``dialect'' of Python. -- Michael Williams

[Michael Williams is looking for a readln() replacement] If you're willing to change the input format to require commas between numbers, you could use the standard built-in function input(), which was put in Python specifically to support this kind of usage. --Guido van Rossum (home page: http://www.python.org/~guido/)

On Wed, May 22, 2002 at 07:42:59AM -0400, Guido van Rossum wrote:
[Michael Williams is looking for a readln() replacement]
If you're willing to change the input format to require commas between numbers, you could use the standard built-in function input(), which was put in Python specifically to support this kind of usage.
I did consider this but: - the quasi-standard way of delimiting data is whitespace -- not commas. - allowing users of programs to interact using input() is A Bad Thing. -- Michael

On Wed, 22 May 2002, Michael Williams wrote:
On Wed, May 22, 2002 at 07:42:59AM -0400, Guido van Rossum wrote:
[Michael Williams is looking for a readln() replacement]
If you're willing to change the input format to require commas between numbers, you could use the standard built-in function input(), which was put in Python specifically to support this kind of usage.
I did consider this but:
- the quasi-standard way of delimiting data is whitespace -- not commas.
- allowing users of programs to interact using input() is A Bad Thing.
Why is it a "Bad Thing" ? If these are the students own programs then security isn't a problem. It's really handy to be able to type expressions for input args instead of having to get out a calculator to figure out the input args for your program. If you do want to restrict the input use something like:
import math def expr( e ): return eval( e, math.__dict__, {} )
Now you can use string.split to get your whitespace delimited args:
map( expr, sys.stdin.readline().strip().split() ) 1 sqrt(2) pi/2 2 3.0 pi [1, 1.4142135623730951, 1.5707963267948966, 2, 3.0, 3.1415926535897931]
If you want some latitude in the choice of separator, you should use re.split -- commas change the evaluation of the string:
map( expr, sys.stdin.readline().strip().split() ) 1,2,3 [(1, 2, 3)] map( expr, sys.stdin.readline().strip().split() ) 1, 2, 3 [(1,), (2,), 3]
map( expr, re.split( '[, \t]', sys.stdin.readline() ) )
If you want some input error checking on the number types of input values, you can use an explicit list of coercions: map( lambda f,x: f(x), [ int, float, expr ], ... You could wrap up your preferred style in a function or use something like this (with all the options):
def readln( source=sys.stdin, prompt='>', sep='[ ,\t]', coerce=None ): ... if source is sys.stdin: inp = lambda : raw_input( prompt ) ... else: inp = source.readline ... args = re.split( sep, inp().strip() ) ... if coerce is None: ... return map( expr, args ) ... else: ... return map( lambda f,x: f(x), coerce, args ) ...
Using the coercion functions lets you input strings without requiring quotes:
readln( coerce=(str,int) ) abc 1 ['abc', 1]
-- Steve Majewski

######### Python code ########### # Either: stdin linestring = raw_input()
# Or: file linestring = f.readline()
linelist = string.split(linestring) x = int(linelist[0]) y = int(linelist[1])
# Or perhaps linestring = raw_input() x,y = string.split(linestring) # x and y are now strings eg. '1' # and '2' x = int(x) y = int(y)
I know people generally say eval() is "bad" but if you're a student writing your own programs to read your own data (not writing some generic thing for over-the-web use) it's unreasonable to expect iron-clad protection against "abuse" at every turn. Just do what works. It's your sandbox, after all. If you're intent upon writing malicious stuff, there's no protecting you from yourself no matter how good a coder you are. If it *is* your sandbox, then splitting space-separated string elements that you know represent numeric data, and evaluating them to lists containing actual integers or floats, is pretty straightforward: def getdata(strdata): return [eval(i) for i in strdata.split()]
getdata('1.2 7e6 -1 10.39') [1.2, 7000000.0, -1, 10.390000000000001]
Given x,y = [1,2] is legal syntax, you can write stuff like: x,y,z = getdata(f.readline()) where readline() gets you the next 3 numbers in character form. x,y and z end up with integer or float values. This problem comes up a lot on the tutor list. To quote Alan, author of a bestselling Python book:
I'd like to convert a string to either a float or an integer ``intelligently", i.e.:
The easy way would be to use eval() All the usual caveats around using eval() (and exec()) apply of course.... That's from earlier today. Another option, if you know the data is either ints or floats, is to apply int() to the string without pre-checking. int('2.0') is an error, as is int('2e3'). So if int can't deal with it, pass it on to float: def getdata2(strdata): rtnval = [] for i in strdata.split(): try: rtnval.append(int(i)) except: rtnval.append(float(i)) return rtnval
getdata2('1.2 7e6 -1 10.39') [1.2, 7000000.0, -1, 10.390000000000001]
If you don't trust the data to be all ints or floats, another level could be added, and/or allow it to crash gracefully (cuz the data must be corrupt). Kirby

I recently got my copy of Stephan Wolfram's new book, 'A New Kind of Science' (2002) which focuses on complexity using computer expriments with cellular automata, ala the Game of Life (but the rules are often even simpler). The early part of the book focuses on grids populated according to simple rules, starting from a top row with just one black cell (the rest white) and working downward. Cells are considered in groups of 3, with the cell directly below the middle one turning white or black based on the mapping rule, i.e. based on how a given combination of 3 (white or black = 8 possibilities) gives a next generation cell in the next row. For example, rule 30 looks like this:
import nks p = nks.Pattern(30) p.sayrule() 111 --> 0 110 --> 0 101 --> 0 100 --> 1 011 --> 1 010 --> 1 001 --> 1 000 --> 0
The combinations on the left are fixed (always the same 8 in that order) whereas the string of 0s and 1s defines a unique binary number from 0 to 255. If I fill with 0s to 8 positions, and ask for 30 in binary, I get:
nks.base2(30,8) '00011110'
...which is the same rule as above. So if in the top row you had 20 whites plus a middle black:
p = nks.Pattern(30,width=20) p.gen '000000000010000000000'
Then successive rows would be:
p.next() '000000000111000000000'
and:
p.next() '000000001100100000000'
Complexity comes in with some rules (including 30) in a dramatic fashion, whereas other rules give terminal, static results, or an expanding fractal of nested, self-similar patterns (which we might consider complex, yet here the term is reserved for more unpredictable, more apparently random patterns). The beginning of the generation process of rule 30 is depicted here: http://www.inetarena.com/~pdx4d/ocn/graphics/nks30a.png and after a few more rows: http://www.inetarena.com/~pdx4d/ocn/graphics/nks30b.png and finally (to a lot more rows): http://www.inetarena.com/~pdx4d/ocn/graphics/nks30c.png This last picture is the default output of:
import nks p = nks.Pattern(30) p.run()
(I have the user then manually opening Povray to render nks30.pov -- an automated render invoked from within Python would be another option (but I like to tweak settings and re-render, so manual is better)). It turns out the Wolfram has been exploiting the randomness in this pattern to serve as a random number generator for Mathematica all this time (pg. 317). So, the pictures above come from using my usual Python + Povray combo. Another approach would be to populate pixels directly using PIL or some similar set of tools. But I wanted quick results through recycling existing code I've already written. Another side effect is we get little spheres in place of the flat squares used in Wolfram's book. That's OK. The patterns are what's important. I basically just use literal strings of 0s and 1s to generate the pictures. I don't keep a whole grid in memory -- each successive row overwrites the one before, so there's only the previous row and the next one to mess with at any given time. The Pattern object accepts any rule from 0 to 255 as input and the run() method creates the Povray file to 388 generations (the default). The first draft of my source code is at: http://www.inetarena.com/~pdx4d/ocn/python/nks.py Note dependencies on povray.py and coords.py -- both posted in the same subdirectory. And of course you need Povray, available from http://www.povray.org/ Kirby

[Kirby Urner]
I recently got my copy of Stephan Wolfram's new book, 'A New Kind of Science' (2002) which focuses on complexity using computer expriments with cellular automata, ala the Game of Life (but the rules are often even simpler).
[Examples snipped]
Cool stuff. This book is on my wish list. How do you like the book overall? And is it relatively easy to translate into Python code, such as the examples you gave? --- Patrick K. O'Brien Orbtech

Cool stuff. This book is on my wish list. How do you like the book overall?
I like that it's written for a broad audience and doesn't assume much specialized knowledge, except maybe in the notes section. It's quite a thick tome -- about 1200 pages not including the index. Lots of illustrations and diagrams (b&w or grayscale). The organization is meticulous, with lots of specific citations to page numbers
And is it relatively easy to translate into Python code, such as the examples you gave?
The examples I gave are the simplest, but are also core/critical. You can get fancier of course, and Python'd be up to the task, although Wolfram is understandably focussed on Mathematica as his implementation system of choice. Kirby
participants (5)
-
Guido van Rossum
-
Kirby Urner
-
Michael Williams
-
Patrick K. O'Brien
-
Steven Majewski