REBOL/Zine Issue 1 Vol 1 REBOL/Zine Issue 1 Vol 1 Date: 15-Jun-2001 Issues: prev | next === Hello! Welcome to the first episode of REBOL/Zine! Hopefully you will enjoy the articles found in this little Zine. It's been a joy to put this first episode together and I hope that many more will come. Programmers have queues. These queues are filled with tasks and projects and things we'd like to get done and many times our queues stretch for miles. There's so much to create with our brains and our time and best efforts. A programmer's queue has a unique property: It's sometimes hard to add things into the queue but things seem to fall out of the queue with great ease! Anyhow, I hope that people will want to add the REBOL/Zine to their queues, send in their comments, corrections, flames, etc. We'll begin a mail section in later issues. And even more importantly, please send us your contributions!! We want your tips, tricks, editorials, novellas about REBOL, you name it. You don't have to submit articles in English either. We'll try and get translations later. Mail your contributions, comments, corrections, bug reports, etc to: mailto:rebolzine@yahoo.com [This episode did not receive thorough editing, so please forgive any grammar or typographical errors you find. ] === REBOL Puzzles and Idioms By Jeff Kreis --- Until when? A very handy REBOL construct is the function UNTIL combined with the lazy evaluators ANY and ALL along with NOT. These can be combined to very easily express many types of problems you may face in REBOL scripts. You want to run UNTIL ALL, or UNTIL ANY of the conditions are met. Likewise you want to continuously loop the code UNTIL NOT ALL the conditions are met, or UNTIL NOT ANY have been met. Below are some examples: go-until-something-happens: does [ until [ any [ exists? %some-file.dat not empty? read pop://user:pass@example.com now/time = 00:00:00 port: wait [listen-port :0:01] ] ] ] This example will run until either some-file.dat exists, some mail is received at a pop mail account, it turns midnight, or something connects to a 'listen-port'. The WAIT is important, because it will prevent this code from "busy looping", exhausting your computer's resources spinning away in a tight loop. A one second waiting period between each loop is plenty of time for most operating systems to deal with all the other processes going on. The WAIT will return NONE if it times out before anyone connects to the listen port, and a NONE will make the loop continue. retry-mail: func [to [email!] msg [string!]][ until [ not all [ error? try [send to msg] not confirm "Error sending. Try again? " ] ] ] The above example, as you would expect, keeps trying to send the message. If it succeeds the loop ends, otherwise it asks if you'd like to try again. If you say no, then ALL fails, which means that NOT ALL is true and UNTIL is then satisfied with the result. You can, of course, use UNTIL and the lazy evaluators to pass back meaningful data: get-someone-on-the-line: does [ return until [ any [ connect-to bob connect-to mary connect-to fred wait 3 ] ] ] You'll have to implement CONNECT-TO, though. The above example will return the first connection that can be established to either Bob, Mary, or Fred. This is an artificial example, but there's many real world situations that are similar. It's interesting to note that you usually think of RETURN as immediately sending back some value, but in this case RETURN may take an indefinite amount of time before it actually DOES RETURN a value. --- Loopy House Here's a simple tip: Blocks can be placed inside other blocks. We all know this, but one interesting thing is that the same block can be placed within another block offset to various locations. For example: house: copy [] rooms: [1 2 3 4 5 6 7] forall rooms [ append/only house rooms ] rooms: head rooms Above we've placed the same block (rooms) inside house at seven different offsets. By doing this, we did not create not seven copies of rooms, but seven separate references into the block rooms. We can see this by changing some of our rooms. forall house [ change/only house/1 join "room-" house/1/1 ] house: head house probe rooms ["room-1" "room-2" "room-3" "room-4" "room-5" "room-6" "room-7"] Above, we've change the state of rooms through the house block. Holding the rooms block at various offsets allows us to easily change it. We can also change alter our container structures (house in this case) with out altering what's contained (rooms). Here we flip our house around without disturbing our rooms: reverse house foreach room house [prin [room/1 #]] print [newline rooms] The above will print: room-7 room-6 room-5 room-4 room-3 room-2 room-1 room-1 room-2 room-3 room-4 room-5 room-6 room-7 In fact, we can continue this scheme in the rooms. Let's add space in these rooms to put things and create doors which lead to other rooms: foreach [room door][ 1 7 2 6 3 5 4 4 5 3 6 2 7 1 ][ change/only room: at rooms room reduce [ room/1 copy [] at rooms door ] ] Okay, that looks a little crazy, but what we're doing is changing each element of rooms to the following: A block containing the name of the room (which we made before), a new block which is a place to put things in the room, and a "door" which is a reference to another room. For example, room 1 has a door to room 7. Room 4 has a door that goes to 4! Alright, so lets see how our rooms work. Let's go to the third location in the house, enter that room, go through the door we find there and put something in the space we made. Can we guess where it will wind up? Here's the steps one by one: use [spot room door next room space][ ;-- Third spot in house: spot: third house ;-- Room is the first thing there room: first spot ;-- which room is this? print first room ;-- Door is the third thing in the room door: third room ;-- The next room is the first thing in the door next-room: first door ;-- Which room is this? print first next-room ;-- The space is the second thing in the next-room space: second next-room ;-- Let's put our kazoo here insert space 'kazoo ] Well, as you should see from what is printed out, the third room in the house is actually room 5. We went through the door and came to room 3. Then we put our kazoo in there. We can verify: rooms/3/2 == [kazoo] Well, the above step by step method is rather tedious, so let's whip together some handy accessor functions. foreach [accessor body][ house-room: [house/:i/1] rooms-room: [rooms/:i] ][accessor func [i [integer!]] body] foreach [accessor body][ room-space: [blk/2] room-name: [blk/1] room-door: [blk/3] door-room: [blk/1] ][accessor func [blk [block!]] body] So, now we can do our previous example a little more succinctly. This time we'll go to the sixth place in the house, enter that room, leave something in the space there, go through the door and put something in the space we get to in the next-room. use [first-room next-room][ insert room-space first-room: house-room 6 'rake insert room-space next-room: door-room room-door first-room 'saw print [ room-space first-room "in" room-name first-room newline room-space next-room "in" room-name next-room ] ] If you experiment, you will find other ways of walking around the rooms. What all this looniness should demonstrate is that you can use blocks to hold the positions of other blocks, including the same block. You can create arbitrarily complex structures with multiple navigation routes. Jeff Kreis jeff at rebol dot com === Posting to Web Forms Tip by Graham Chiu When you write a Rebol script that posts data to a web form, you have to know all the variable names, including the hidden variables. With large complex web pages, it can be rather tedious trying to find all the names, and values. Sterling wrote a script in 1999 that analyses web forms ( http://www.rebol.org/web/parse-form.html ) but sometimes it doesn't work, and if the form you are accessing is protected by a cookie, then you're out of luck. Also, some hidden values in forms are generated dynamically by javascript and so are not accessible to his script. Luckily there's a very simple solution. There is another script from that era ( http://www.rebol.com/library/html/cgidump.html ) that dumps all the cgi variables to the browser. I modified it to display the form variables when sent by a POST as well as a GET, and saved it to my server as http://www.compkarori.com/cgi-local/cgidump.r Now, all I need do is to save the form I'm looking at to my hard drive using my browser's 'Save As' function, edit it so that the action of the form now points to my cgidump.r script: