Newsgroups: comp.lang.scheme,comp.lang.misc,comp.lang.perl,comp.lang.tcl
Path: cantaloupe.srv.cs.cmu.edu!das-news2.harvard.edu!news2.near.net!news.mathworks.com!news.alpha.net!uwm.edu!rutgers!njitgw.njit.edu!funcity!aaron
From: aaron@funcity.njit.edu (Aaron Watters)
Subject: some exercises Re: What language would you use?
Message-ID: <1994Nov11.170007.8635@njitgw.njit.edu>
Sender: news@njit.edu
Nntp-Posting-Host: funcity.njit.edu
Organization: New Jersey Institute of Technology
References: <SCHWARTZ.94Nov4172641@roke.cse.psu.edu> <ZIGGY.94Nov7180245@biere.ai.mit.edu> <SCHWARTZ.94Nov10165109@roke.cse.psu.edu>
Date: Fri, 11 Nov 1994 17:00:07 GMT
Lines: 224
Xref: glinda.oz.cs.cmu.edu comp.lang.scheme:11175 comp.lang.misc:19054 comp.lang.perl:38679 comp.lang.tcl:21716

In article <SCHWARTZ.94Nov10165109@roke.cse.psu.edu> schwartz@roke.cse.psu.edu (Scott Schwartz) writes:
>Tom Christiansen posted three simple exercises recently; they ought to
>take a few minutes to solve (in perl), but I didn't see any responses.
>Were they beneath contempt, or just too difficult to do in scheme?

I don't think they'd be hard in scheme, but I'd like to see.
Guido Van Rossum posted solutions in python to the python
mailing list (he doesn't have news access at the moment).  I include
them for him since I think he would have cross posted if he had
been able.
=========== CUT, begin the voice of Guido Himself
[I apologize beforehand -- this should go to all mentioned newsgroups
but I can't post news from here so I'll mail it to Tom and to the
Python mailing list, which is gatewayed into comp.lang.python.  Maybe
Tom can quote this when posting his promised Perl solutions?]

Tom Christiansen wrote:

> If you're really into benchmarks, I'd love it if someone were to code up
> the following problems in tcl, python, and scheme (and whatever else you'd
> like).  Separate versions (one optimized for speed, one for beauty :-) are
> ok.  Post your code so we can time it on our own systems.

OK, I'll bite.  Here are three samples coded in Python.  I spent at
most an hour and a half coding these three files -- with
interruptions.  I'm actually not horribly interested in performance
comparisons, although I used a few tricks to speed things up
(actually: to avoid slowing them down).  I wrote these solutions
because I think that a collection of problems like these coded in a
number of languages may give people a better idea of what "real"
programs in the various languages look like.  Of course, Tom's choice
of problems is bound to favor Perl, but not extremely so.  Perhaps I
should counter with a set of problems that I think favor Python.

======================================================================
regextest.py:
======================================================================
#! /usr/local/bin/python

# 1)  Regular Expressions Test
# 
#     Read a file of (extended per egrep) regular expressions (one per line), 
#     and apply those to all files whose names are listed on the command line.
#     Basically, an 'egrep -f' simulator.  Test it with 20 "vt100" patterns
#     against a five /etc/termcap files.  Tests using more elaborate patters
#     would also be interesting.  Your code should not break if given hundreds
#     of regular expressions or binary files to scan.  

# This implementation:
# - combines all patterns into a single one using ( ... | ... | ... )
# - reads patterns from stdin, scans files given as command line arguments
# - produces output in the format <file>:<lineno>:<line>
# - is only about 2.5 times as slow as egrep (though I couldn't run
#   Tom's test -- this system, a vanilla SGI, only has /etc/terminfo)

import string
import sys
import regex
from regex_syntax import *

regex.set_syntax(RE_SYNTAX_EGREP)

def main():
	pats = map(chomp, sys.stdin.readlines())
	bigpat = '(' + string.joinfields(pats, '|') + ')'
	prog = regex.compile(bigpat)
	
	for file in sys.argv[1:]:
		try:
			fp = open(file, 'r')
		except IOError, msg:
			print "%s: %s" % (file, msg)
			continue
		lineno = 0
		while 1:
			line = fp.readline()
			if not line:
				break
			lineno = lineno + 1
			if prog.search(line) >= 0:
				print "%s:%s:%s" % (file, lineno, line),

def chomp(s):
	if s[-1:] == '\n': return s[:-1]
	else: return s

main()
======================================================================
sortingtest.py:
======================================================================
#! /usr/local/bin/python

# 2)  Sorting Test
# 
#     Sort an input file that consists of lines like this
# 
#         var1=23 other=14 ditto=23 fred=2
# 
#     such that each output line is sorted WRT to the number.  Order
#     of output lines does not change.  Resolve collisions using the
#     variable name.   e.g.
# 
#         fred=2 other=14 ditto=23 var1=23 
# 
#     Lines may be up to several kilobytes in length and contain
#     zillions of variables.

# This implementation:
# - Reads stdin, writes stdout
# - Uses any amount of whitespace to separate fields
# - Allows signed numbers
# - Treats illegally formatted fields as field=0
# - Outputs the sorted fields with exactly one space between them
# - Handles blank input lines correctly

import regex
import string
import sys

def main():
	prog = regex.compile('^\(.*\)=\([-+]?[0-9]+\)')
	def makekey(item, prog=prog):
		if prog.match(item) >= 0:
			var, num = prog.group(1, 2)
			return string.atoi(num), var
		else:
			# Bad input -- pretend it's a var with value 0
			return 0, item
	while 1:
		line = sys.stdin.readline()
		if not line:
			break
		items = string.split(line)
		items = map(makekey, items)
		items.sort()
		for num, var in items:
			print "%s=%s" % (var, num),
		print

main()
======================================================================
systemtest.py:
======================================================================
#! /usr/local/bin/python

# 3)  System Test
# 
#     Given a list of directories, report any bogus symbolic links contained
#     anywhere in those subtrees.  A bogus symbolic link is one that cannot
#     be resolved because it points to a nonexistent or otherwise
#     unresolvable file.  Do *not* use an external find executable.
#     Directories may be very very deep.  Print a warning immediately if the
#     system you're running on doesn't support symbolic links.

# This implementation:
# - takes one optional argument, using the current directory as default
# - uses chdir to increase performance
# - sorts the names per directory
# - prints output lines of the form "path1 -> path2" as it goes
# - prints error messages about directories it can't list or chdir into

import os
import sys
from stat import *

def main():
	try:
		# Note: can't test for presence of lstat -- it's always there
		dummy = os.readlink
	except AttributeError:
		print "This system doesn't have symbolic links"
		sys.exit(0)
	if sys.argv[1:]:
		prefix = sys.argv[1]
	else:
		prefix = ''
	if prefix:
		os.chdir(prefix)
		if prefix[-1:] != '/': prefix = prefix + '/'
		reportboguslinks(prefix)
	else:
		reportboguslinks('')

def reportboguslinks(prefix):
	try:
		names = os.listdir('.')
	except os.error, msg:
		print "%s%s: can't list: %s" % (prefix, '.', msg)
		return
	names.sort()
	for name in names:
		if name == os.curdir or name == os.pardir:
			continue
		try:
			mode = os.lstat(name)[ST_MODE]
		except os.error:
			print "%s%s: can't stat: %s" % (prefix, name, msg)
			continue
		if S_ISLNK(mode):
			try:
				os.stat(name)
			except os.error:
				print "%s%s -> %s" % \
				      (prefix, name, os.readlink(name))
		elif S_ISDIR(mode):
			try:
				os.chdir(name)
			except os.error, msg:
				print "%s%s: can't chdir: %s" % \
				      (prefix, name, msg)
				continue
			try:
				reportboguslinks(prefix + name + '/')
			finally:
				os.chdir('..')

main()
======================================================================

--Guido van Rossum, CWI, Amsterdam <mailto:Guido.van.Rossum@cwi.nl>
<http://www.cwi.nl/cwi/people/Guido.van.Rossum.html>



