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 ?