Q L H A C K E R ' S J O U R N A L
===========================================
Supporting All QL Programmers
===========================================
#32 April 2000
The QL Hacker's Journal (QHJ) is published by Tim
Swenson as a service to the QL Community. The QHJ is
freely distributable. Past issues are available on disk,
via e-mail, or via the Anon-FTP server, garbo.uwasa.fi.
The QHJ is always on the look out for article submissions.
QL Hacker's Journal
c/o Tim Swenson
2455 Medallion Dr.
Union City, CA 94587
swensont@lanset.com swensont@geocities.com
http://www.geocities.com/SiliconValley/Pines/5865/index.html
Editor's Forumn
First off, thanks to Herb Schaaf for providing most of the
text for this issue. When Herb gets a hold of a problem, he
does not let go until he knows all there is to know.
Secondly, I wanted to bring up something that has been
discussed in the ql-users mailing list. I can't remember
who started it, but there was a discussion on what software
QLers would like to see next for the QL. The obvious, and
non-trivial, stuff was mentioned; a TCP/IP stack, a HTML
browser, color graphics and software to use it, and so on.
As much as these are nice, the level of effort to create
them is beyond most QL programmers. What I would like to
see is a list of more day-to-day applications with a much
lower level of effort. Once a list is created, QL
programmers could see what others want and take a try at
writing the programs. As much as I write software for my
own needs, it's nice to know when others also find it
usefull.
To help this idea along, I'm willing to act as the go
between the user and the programmers. I'll take program
suggestions from users and list them on my web page. From
there, programmers can volunteer to work on a program, which
I can coordinate so that two people are not working on the
same program. So, if you have some ideas of what software
you would find usefull, send it to me.
Befunge
I recently found a new language that has an implementation
that will run on the QL - Befunge. Befunge is the first
two-dimensional language. This means that the Program
Counter, instead of just going down a line of code, can move
up, down, left, and right. This is easily done by making
each Befunge command a single character.
Like FORTH and Postscript, Befunge is a stack oriented
language. Each command either is data to be put on the
stack, or is an operator to do something with what ever is
on the top of the stack. Numbers are limited to input via
single digits, but may be expanded by manipulating the
stack. To get 23, you would push 7 on the stack, push 3 on
the stack, do a mulitply command (leaving 21 on the stack),
push 2 on the stack, and do an add command (leaving 23 on
the stack). In Befunge it would look something like this:
73*2+
To then print out the value on the stack, just use the
integer print command, the period (.).
Befunge code space is an 80x24 array of characters. The
Program Counter starts off in the upper left hand corner and
moves from left to right. It continues this way until it
hits the end of a line and wraps around to the start of the
same line or until it hits a command that changes its
direction.
I would love to talk about how fun and easy the language is,
but I still have not quite figured it out. The examples
used in the Befunge language documentation look easy, but
the example code that comes with it is still causing my
brain to hurt.
Now as far as running Befunge on the QL - let me start with
a little history on how I ran across Befunge. The Fall 1999
edition of "The Perl Journal" had the results of the latest
Obfuscated Perl Contest. One of the entries was a Befunge
interpreter. Since the code was rather cryptic (as is all
Obfuscated programs) and I did not know if there were any
Perl5-isms in the code. I tried to get a copy from "The
Perl Journal" web page. Having forgot my subscribers
account name and password, that did not go well. Time to
try Yahoo! and do a search on Befunge. Look, a nice Befunge
web page, with documentation and a list of implementations.
One of the implementations is in C. Not being a C person, I
was not up to the tasking of porting it to the QL. Then I
saw the answer, an implementation in Z-code.
For those that don't know, Z-code is a data file used for
the old Infocom adventure games. Infocom adventure games
are written in the language Inform, then compiled, and then
run with ZIP (Z-code Interpreter). A version of ZIP has
been ported to the QL and I've used it a number of times
over the years.
I downloaded the Z-code implementation of Befunge, unzipped
it and fired off ZIP with zbefunge.Z5 as the data file.
After some initial complaining about not been run on a
"real" Z-code interpreter, I was able to the the main screen
up. The Z-Code version has a built in editor, from which
you can then run a program.
I typed in the "Hello World" example program, hit the
function key to run the code and "Hello World" was printed
out on the screen. It looks like it works.
Once nice feature of the Z-code implementation of Befunge,
is a debug option. Using this option, you can step through
the Befunge program watching the program counter move about.
As the program runs, you can see, at the bottom of the
screen, the top 6-or-so values on the stack. You can also
toggle over to a screen where the output is shown.
Using the debug feature I am starting to get a hang on how
the Program Counter moves about.
This implementation also allows for loading and saving
Befunge programs. I tried loading some of the programs that
come with Befunge, but the interpreter had an error. I
then typed in the "Hello World" program and saved it. When
I loaded it back in, I got an error, but the program then
showed up fine. It looked at the saved file and it has a
bunch of extra non-ASCII stuff. It looks like the saving
and loading feature has some problems, but it does seem to
work.
The main web page for Befunge is:
http://www.cats-eye.com/befunge/
The web page to get the Z-code implementation of Befunge is:
http://www.meta.demon.co.uk/zbefunge.html
Here is a few example Befunge programs:
Listing 1: (Hello World)
v
>v"Hello world!"0<
,: ^_25*,@
Listing 2: (Factorial)
v
>v"Please enter a number (1-16) : "0<
,: >$*99g1-:99p#v_.25*,@
^_&:1-99p>:1-:!|10 <
^ <
Listing 3: (To Upper)
v, < < <
>~:"a"1-`!#^_:"z"1+`#^_"aA"--^
Perlpull prose, (required reading)
by Herb Schaaf
"a perl of great precise(sic)" , but limited accuracy.
"Optimized for text" they say of perl, that wonderful
Swiss-army chainsaw programming language. But I've had
great fun with numbers, discovering the mathematical
abilities of perl for the QL, version 4.036 as ported over
by Jonathan Hudson. We get answers to math problems in
double precision, carried out to 14 or 15 signifigant
figures, similar to ABACUS. When I tried Tim's dice.pl
(QHJ#30), and put in 2 die with 3 sides, or 3 die with 2
sides (like flipping coins), I got very strange answers.
Things ($totper) didn't add up to 100%, but would come out
under or over. I discussed this with Bill Cable while at
the East Coast QL show. He got good and reasonable answers
on his PC laptop using Perl 5, but with QL perl 4 under QLAY
he was able to get the same funny answers as I had found.
Turns out to be in the exponentiation function. This
creates a floating point number, and these can cause trouble
when used for counting or comparisons. Exponentiation uses
natural logarithms and rounding errors in the 15th decimal
place cause the bogus answers. I wrote a perl subroutine
using an algorithm similar to the one in the power function
from "The C Programming Language" K&R 2nd ed., section 1.8,
page 27 which uses integer values and so far has given the
"right" answers. Perl does not use typecasting, so we can't
declare (int)power. Nor can I try "use integer;" to see how
that works.
In the dice.pl program I changed from the exponentiation
operation to a call to the power subroutine and then the
answers came out as expected.
But you might ask; how can we make a call to a function or
procedure in perl? The answer is the use of the perl
keyword "sub" before the name of the function block which is
appended to the listing, and the use of the ampersand "&"
before the name of the function to call it. Another choice
is to write the subroutine as a file which you add to your
library and can then pull it in when wanted by simply asking
your main program to "require" it.
Perlpull prose - my adventures with perl pulls prose out of
me.
Purple prose - expletives replete when recursing a perl
problem.
Here's the block to append to dice.pl:
sub power {
local($base,$exponent) = @_;
$power = 1 - ($base == 0);
if (($base == 0) && ($exponent == 0)) {
$power = "NaN";
}
else {
while( $exponent > 0) {
$power *= $base;
$exponent--;
}
}
return $power;
}
To put it into the library, think of a filename for it; (I
used lib_power.pl) and add:
1;
as a final last line.
In QLHJ#30 the three instances of the expression:
($sides**$num_die)
are replaced with the expression:
(&power($sides,$num_die))
Now you can either add the power subroutine (without the
final 1;) to the dice.pl listing, or you can pull in the
library version (with the final 1;) by having a line added
at the beginning of dice.pl that reads:
require "power.pl";
which will pull it in from your lib_ subdirectory.
To see how the values compare, try this compower.pl program:
#!/usr/bin/perl
# compower.pl for QL perl 4
# H L Schaaf August 21, 1999
# to compare the results of exponentiation in perl with# a method that multiplies an integral number of times.
$around = 1;
$log_limit = log(2**1023);
while($around){
print "\fThis is round ",$around;
print "\n\n\t please ENTER a number for the base ";
$base = &inkey.;
chop $base;
if($base) { $safe_size =
&abs(int($log_limit/(log(&abs($base)))));
}
else {
$safe_size = 2**1023;
}
print "\n\t(exponents larger than ",$safe_size," are
probably too large)";
print "\n\n\t please ENTER a positive integral number
for the exponent ";
$exponent = &inkey.;
chop $exponent;
$power = &power($base,$exponent);
print "\n\n\t integral power ",$power;
$float_power = $base**$exponent;
print "\n\n\t floating point power ",$float_power;
print "\n\t -------------------------";
print "\n\n\t difference is ",
$power-$float_power;
print "\n\n\t ENTER for another, ESC (at any time) to
quit";
&inkey;
}
continue {
$around ++ ;
}
sub power {
local($base,$exponent) = @_;
$power = 1 - ($base == 0);
if (($base == 0) && ($exponent == 0)) {
$power = "NaN";
}
else {
while( $exponent > 0) {
$power *= $base;
$exponent--;
}
}
return $power;
}
sub inkey {
sysread(STDIN,$inkey,1);
if (ord($inkey) == 27) {
print "\b \n\n\n\t\t";
exit;
}
return $inkey;
}
sub sgn {
local($n) = @_;
return ($n <=> 0);
}
sub abs {
local($n) = @_;
return ($n * &sgn($n));
}
Other folks have written all sorts of things for perl that
can be "required" and used. Two numerically interesting
examples, bigint.pl and bigfloat.pl (which itself pulls in
bigint.pl), are in the library provided by Jonathan Hudson.
I found them fun to noodle with, so why not give them a try
if you're into math and want to see results carried out with
great precision. You can set the number of significant
digits to be "arbitrarily (?)" large.
Here is the result of my noodling around:
#!/usr/bin/perl
# bigflopintdemo.pl bigfloat.pl and bigint.pl in QL perl 4
# H L Schaaf August 21, 1999
print " a small demo of big floating point and big integer
operations in perl";
print "\n please wait for required module(s) to be pulled in
from the library";
require "bigfloat.pl";
$around = 1;
while($around){
print "\f this is round ",$around;
print "\n please ENTER the first number ";
$n1 = &inkey.;
chop $n1;
$valid_answer = 0;
print "\n choose an operation by touching the
appropriate key\n";
print "\n [P]lus, [M]inus, [T]imes, [D]ivided by ";
print "\n [R]aise to an integral power, [S]quare root
(these take time)";
print "\n\t\t [G]reatest common denominator\t";
while( !$valid_answer){
$op = &inkey;
if ($op =~ /[pPmMtTdDrRsSgG]/) {$valid_answer = 1;}
print "\b \b";
}
print "\n\n";
if($op =~ /[sSdD]/) {
print "\n How many signifigant digits wanted ? ";
$sig_digits = &inkey.;
chop $sig_digits;
}
else {
$sig_digits = 1;
}
if ($op =~ /[sS]/) {
print "\n\t\t please wait \n";
$started = time;
$f = &fsqrt($n1,$sig_digits);
}
else {
print "\n please ENTER the second number ";
$n2 = &inkey.;
chop $n2;
if ($op =~ /[rR]/) {print "\n\t\t please wait \n"; }
if ($op =~ /[pP]/) {$f = &fadd($n1,$n2,$sig_digits); }
if ($op =~ /[mM]/) {$f = &fsub($n1,$n2,$sig_digits); }
if ($op =~ /[tT]/) {$f = &fmul($n1,$n2,$sig_digits); }
if ($op =~ /[dD]/) {$f = &fdiv($n1,$n2,$sig_digits); }
if ($op =~ /[gG]/) {$f = &bgcd($n1,$n2) ; }
if ($op =~ /[rR]/) {$started = time; $f =
&bpow($n1,$n2); }
}
if ($op =~ /[rRsS]/) {
$elapsed_time = time - $started;
print "\n\t that took about ",$elapsed_time,"
second", (($elapsed_time == 1) ? " " : "s"),"\n";
}
print "\n",$f,"\n";
print "\n ",&withdecimal($f);
print "\n touch ENTER for another demo or ESC (at any
time) to exit ";
&inkey;
print "\f";
}
continue {
$around ++ ;
}
sub inkey {
sysread(STDIN,$inkey,1);
if (ord($inkey) == 27){
print "\b \n\n\n\t\t";
exit;
}
return $inkey;
}
sub withdecimal {
local($bigfloat) = @_ ;
local($number,$exponent) = split('E',$bigfloat);
$decimal_place = (length($number)) + $exponent;
if ($exponent > 0) {
$number = $number.("0" x ($exponent));
}
if ($decimal_place>1){
$bigfloat_with_decimal =
substr($number,0,$decimal_place)
.".".substr($number,$decimal_place);
}
else {
$number_lead =substr($number,0,1)."0.";
$zeros = "0" x (1 - $decimal_place);
$bigfloat_with_decimal =
$number_lead.$zeros.substr($number,1);
}
return $bigfloat_with_decimal;
}
sub bpow {
local($bbase,$bexponent) = @_;
$bpower = 1 - ($bbase == 0);
if(($bexponent == 0) && ($bbase == 0)) {
$bpower = "NaN";
}
else {
while ($bexponent) {
$bpower = &fmul($bbase,$bpower);
$bexponent--;
}
}
return &fnorm($bpower);
}
Imagine how we could embellish this by adding trig and other
math functions. We could even create a general purpose
("general perlplus"?) scientific calculator with store,
recall, memory registers and such; maybe it has been done
already and is on CPAN. Of course half the fun is in
writing a program yourself, and the other half is debugging
and getting it to work.
I tend to think in in BASIC, my first programming language,
so have tried to find ways to get results in perl that are
comparable to some of the S*BASIC commands. Here are some
equivalents that seem to work:
INKEY$(-1)
I like to have an interactive menu sometimes, and just want
the same action as we enjoy with INKEY$(-1) in S*BASIC. I
finally found a way to do it on the QL in perl. This gets a
key from the user without them having to touch ENTER. The
perl keyword is sysread.
inkey.pl is an example that will detect the ESC key from the
user
# sysread as a way to read inkey without use of ENTER
# inkey.pl
while(1){
sysread(STDIN,$raw,1);
if ( ord($raw) eq 27 ) {
print "\b \t";
exit;
}
else {
$ans = $raw.;
}
chop($ans);
print "\n";
print length($ans);
print "\n",$ans,"\n";
if (length($ans)==1){
if ($ans =~ /[yY]/) {
print "\n That's a Yes \n";
}
if ($ans =~ /[nN]/) {
print "\n That's a No \n";
}
}
}
I can't edit the first digit of numbers when I use this
inkey.pl, maybe there is a work-around like the getch and
ungetch in C ? When using QTPI as a link to UNIX at the
University of Delaware, this inkey subroutine behaves
differently; it still works, but seems to be anticipated or
read-ahead in the script. There's probably a better way;
how would you do it?
REPeat END REPeat$around = 1;
while ($around) { }
In my explorations of perl programs I often want to keep
trying different inputs to see how things go, without having
perl exit after my first exploration. By putting everything
inside in braces after a while(1) I'm able to get the effect
that REPeat , END REPeat has.
exit;
works anytime to break out of the while($around), so we can
test for some condition (like the ESC key being touched) to
end a session.
I use the continue block to keep count of the trips around
the while loop.
continue {
$around ++;
}
I believe that nearly the same effect (except for the
continue) could be accomplished very simply with the -n or
-p switch on the command line. And of course we could use
for loops too. TMTOWTDI or tim-toady as they say in perl;
"There's More Than One Way To Do It."
CLS
print "\f";
or formfeed, does the trick when we want a "clean slate".
DATE
example: $now = time;
the keyword in perl is time.
perl's calendar starts in 1970 instead of the QL's 1961;
I used the time function in bigflopintdemo.pl to see how
long it took to extract square roots or raise to integral
powers with the bigfloat.pl and bigint.pl libraries. It
took 220 seconds to raise 2 to the 1024th power, and 11
seconds to get the square root of 2 to 100 digits, with the
Super Gold Card. With a Gold Card it took 681 seconds for
the power and 28 seconds for the root. It took 2 seconds
for the power and 0 for the root with Perl 5.005_02 running
under sun 4 solaris using my University of Delaware UNIX
account via QTPI. It was nice to see the same program work
on both QL's and under the UNIX setup.
When running compower.pl the UNIX results were equal with no
difference between the exponentiation operator and the power
subroutine. So perhaps Perl 5 does something with integer
exponentiation that Perl 4 does not.
PAUSE(power_cycles)
sleep(seconds);
perl's sleep is measured in seconds; the QL PAUSE counts the
power line cycles. If no parameter is given both will wait
forever. perl has alarm(seconds); but I haven't sussed it
out yet. How do you regain control in perl if you've done
sleep(); ?
CODE("character in quotes") in the QL
is equivalent to ord($chr) in perl
this returns the ASCII code for a character.
chr($ascii) in perl is equivalent to CHR$(ascii) in the QL
and returns the character for the ASCII code.
We could start a module of these equivalents and conversions
between QL S*Basics and perl and put them into our library
as well. We might also try to build an associative array
%QLBASIC_perl_hash following the example of Bill Cable's
"English-Spanish convertor" and see how they work and learn
how to add more terms to the list.
perl has other ways to pull in snippets, scripts, etc. and I
wonder how the keywords 'do', 'eval', and 'use' work.
Anybody want to give us some examples? Oh, there's a LABEL:
concept in perl that uses 'next', 'last', and 'redo'.
Anybody want to show us how those work? How about the
termcap.pl items, can we control the cursor, do ASCII
graphics ? What about bigrat.pl ?
What perl features have you found fun or useful ?