FREE Subscription to Dr. Dobb’s Digest: Same Great Content, New Digital Edition
Site Archive (Complete)
Email
Print
Reprint

add to:
Del.icio.us
Digg
Google
Furl
Slashdot
Y! MyWeb
Blink
January 01, 1993

Hypercard Database Tuning

(Page 3 of 3)
APR93: HYPERCARD DATABASE TUNING

HYPERCARD DATABASE TUNING

Linked lists for greater performance

Jeff Elliott

Jeff, an engineer and freelance writer whose work has appeared in EIR magazine and the San Francisco Chronicle, can be contacted at 6945 Hutchins Ave., Sebastopol, CA 95472.


Everybody knows that large HyperCard stacks are supposed to be slow. It's common knowledge, part of the same musty storehouse of wisdom that reminds you never to swim after a full meal, run while holding scissors, or believe a politician during an election year. If everybody knows it, it must be true, right?

Common knowledge isn't always true, of course, except perhaps the bit about politicians. It certainly isn't true that large HyperCard stacks have to be sluggish. HyperCard may not match the speed of a true database engine like FoxBase, but HyperCard's find is no slouch either, taking only 20 seconds to search my database of over 3600 cards and find 190 matches--that's an impressive speed of about 180 cards per second. Not bad, but faster is always better, and searches can be up to 20 times faster if you use linked lists.

What are Linked Lists?

Linked lists (as in links of a chain) tie together information scattered through a database. This information can be text in a particular field, the highlight of a button, or anything el e important enough to need frequently. A recipe database might have a "main ingredient" field that specifies whether a recipe uses chicken, broccoli, or other meats or vegetables. Each unique entry in this field would be linked together: fried chicken linked to General Tso's chicken, and so on. Another set of links may connect a popup that indicates the cuisine type: Chinese, Italian, or Mexican. As the different links thread their way through the stack they will sometimes overlap, as Chinese and chicken do in the recipe for General Tso's chicken.

In addition to reducing search time because fewer cards have to be examined, linked lists can also be a powerful navigational tool. Each item linked provides an opportunity for the user to branch off in a new direction following a link trail. And, to give the browser as much control as possible (after all, this is a Macintosh), there can be a second link for each item, providing the freedom to move backward through the chain as well as forward.

The benefits of linked lists--faster searches and easier browsing--can be lost if it is hard to find one of the cards with the link you need. A good solution is to maintain an index of all the linked strings. This index can be kept in a scrolling field from which the user may select a linked topic. The recipe stack, for example, would benefit from both a cuisine index and main-ingredient index, allowing simple and quick access to linked lists covering different topics.

HyperLinks

Unfortunately, the only linking capability built into HyperCard is the linkTo option found in the button dialog, and it's unusable for linked lists. For starters, linkTo is one of the few things that cannot be used in a HyperTalk script. To link, therefore, the user must change to the button tool, double-click on a card-level button to invoke the dialog, click on linkTo, find the card to link with, click OK, and finally choose the browse tool again.

Together, the link function and insert-Link subroutine (Listing One , page 41) provide a better solution. These routines make the linking process invisible to the user and create links for each topic on-the-fly. The link function also builds and maintains an index of all linked topics. The linking is fast, requiring only about 15 ticks to insert a new card into the chain, quick enough that it's probably not necessary to change the cursor or otherwise notify the user.

The links are stored in one or more hidden fields on each card. The card ID is used for the links because it is guaranteed not to change if cards are added or sorted. All of the links can be stored on different lines of a single field, but there are some performance trade-offs when HyperCard has to work harder to find the link. Figure 1 compares the speed of fetching a link when it is alone in a field, when it is one of several items on a line, and when it is on a separate line. When speed is important, it's best just to have a single link in the field.

Figure 1 also compares the performance of HyperCard's find when there are only two characters in a search string vs. three or more characters. It's well known that triplet searches are quicker, but I was surprised to discover that they're over 20 times faster. This is another advantage to using linked lists: Because we're using card IDs instead of find, there's no performance difference if there are fewer than three characters in the search string.

Linking Your Stack

To build your own linked lists, drop the link and insertLink functions into the stack script of your HyperCard stack. As mentioned previously, there are advantages to using the linked lists with an index. They provide fast search access to a card with the desired link, and also a simple way for the user to select a linked topic.

These routines expect you to have a card named index with both a back ground field hiddenIndex and a card field index. Field index only has the selectable text strings, and hiddenIndex (which obviously should be a hidden field) has three items on each line: the string that the user sees, the ID of the first card in the link, and the count of how many cards share the link. The index field is not technically necessary, but it keeps things neat. Both fields must have the "don't wrap" and "lock text" options turned on.

When the user clicks on a line in the index field, the script looks up the string in hiddenIndex and takes the user to the first card in the linked list. Listing Two (page 41) is the script for field index.

The link handler has four parameters: name, linkf, n, and i. The name parameter is a string to search for in field hiddenIndex on the index card. If this name is not found, it is added to the index. Otherwise, this card is linked into the chain of other cards that share the name string in this field. The next three parameters, linkf, n, and i, all refer to the same background field on the card being linked. linkf is the name of the field that holds the links, n is the line number, and i the item number for the forward link. The backward link follows as item i + 1.

If you're only linking a single field, you may want to simply have an Add Link button that prompts for a string (new or existing) and creates a new linked card for the user. Listing Three (page 41) shows what the script for this button might look like.

If you have more than one object to link, you should call link whenever data has been entered; you don't want to clutter the cards with a multitude of special buttons. When you are linking text fields, the closeField handler is an ideal time to call link because this message is sent only when something has been entered or changed in the field.

Once the links are in place, you can use them for navigation. In my HyperCard stacks, each field and group of buttons that is linked has an adjoining pair of arrows that allow the user to branch off and follow a particular link. The scripts for these navigational arrows should read something like:

  go card ID item 1 of line 3 of field links

where the item 1 of line 3 is the next link in that particular chain.

_HYPERCARD DATABASE TUNING_ by Jeff Elliott

[LISTING ONE]

-- insertLink: Subroutine for Link. Links this card into the chain, both -- forwards and backwards. Does not modify the index. -- Arguments: -- root card id of the first card in the chain -- linkf name of the bg field on this card that holds the links -- n line number for links in linkf -- i item number for FORWARD link in line n -- returns: 0: noErr, -1: no forward link, -2: no back link function insertLink root,linkf,n,i -- BACKWARD link always follows FORWARD link as the next item in line n of field linkf put i + 1 into j put the short ID of this card into myself put item i of line n of field linkf of card id root into forward -- set vars; who was the put item j of line n of field linkf of card id root into backward -- root pointing to? if there is not a card id forward then return -1 if there is not a card id backward then return -2 put myself into item j of line n of field linkf of card id root -- root's back is always myself if forward is myself then -- it's just the 2nd card put myself into item i of line n of field linkf of card id root -- so my forward is also root else -- no more root changes put myself into item i of line n of field linkf of card id backward -- his forward is me end if -- finally, make links for myself put backward into item j of line n of field linkf -- my back is previously oldest put root into item i of line n of field linkf -- my next card is the root return 0 end insertLink -- Link: called from the card that's being linked. Before a link is added, -- we must first lookup the name in the index. If this name isn't found, -- then we create an index entry. Otherwise, we link this card to the others. -- Expects: card "index" with background field "hiddenIndex" and card field -- "index" -- Arguments: name, string to lookup in (or add to) the index; -- linkf, n, and i are the same as for subroutine insertLink. -- returns: a success or failure message function link name, linkf, n, i put the short ID of this card into myself go card "index" find whole name in field "hiddenIndex" -- is this name in the index? if the result is "not found" then -- no, so add it to index -- first in the link, so create index entries -- format for hiddenIndex: string, card ID, count get the number of lines in field "hiddenIndex" put name & "," & myself & "," & 1 B into line it + 1 of field "hiddenIndex" -- tack it onto the end -- card field index only has the strings get the number of lines in cd field "index" put name into line it + 1 of cd field "index" sort cd field "index" -- make it pretty go back -- as the only card, it is just linked to itself put myself & "," & myself into line n of field linkf else -- the name's in the index, so we link to other cards add 1 to item 3 of line word 2 of the foundline B of field "hiddenIndex" -- increase link count in index put item 2 of the value of the foundline into root go back -- return to card being linked -- we're ready to link up with the other cards if insertLink (root,linkf,n,i) is not 0 then return "The links are damaged or missing." -- error exit end if end if return "Link successful." -- everything OK, normal exit end link

[LISTING TWO]

on mouseUp -- works only in HyperCard 2.0 or later find whole the value of B the clickline in field "hiddenIndex" go card ID item 2 of B the value of the foundLine end mouseUp

[LISTING THREE]

on mouseUp ask "Name to add:" if it is not empty then doMenu "New Card" put it into fld "myName" put link (it,"links",1,1) end if end mouseUp


Copyright © 1993, Dr. Dobb's Journal

Previous Page | 1 | 2 | 3
TOP 5 ARTICLES
No Top Articles.
DR. DOBB'S CAREER CENTER
Ready to take that job and shove it? open | close
Search jobs on Dr. Dobb's TechCareers
Function:

Keyword(s):

State:  
  • Post Your Resume
  • Employers Area
  • News & Features
  • Blogs & Forums
  • Career Resources

    Browse By:
    Location | Employer | City
  • Most Recent Posts:
    MEDIA CENTER  more
    NetSeminar
    Modernize your Development by Moving Build and Code Quality Upstream
    Moderated by Jon Erickson, Editor-in-Chief of Dr. Dobb's, this interactive panel discussion brings industry experts Anders Wallgren, CTO of Electric Cloud and Gwyn Fisher, CTO of Klocwork together for a candid discussion of the cost savings, productivity and quality benefits that can be achieved by stabilizing builds and code quality as early in the development cycle as possible.

    The reality of today's development environment - geographically distributed teams, the use of Agile development practices, increasing application complexity, etc. - is straining the viability of the traditional coding, build and release process. To stay ahead of the curve, development teams are modernizing their approach to dealing with these issues, and as a result are achieving new levels of development productivity. Register for the webcast.
    Date: Wednesday, July 15, 2009
    Time: 11 am PT/2 pm ET
    Modernize your Development by Moving Build and Code Quality Upstream
    Moderated by Jon Erickson, Editor-in-Chief of Dr. Dobb's, this interactive panel discussion brings industry experts Anders Wallgren, CTO of Electric Cloud and Gwyn Fisher, CTO of Klocwork together for a candid discussion of the cost savings, productivity and quality benefits that can be achieved by stabilizing builds and code quality as early in the development cycle as possible.

    The reality of today's development environment - geographically distributed teams, the use of Agile development practices, increasing application complexity, etc. - is straining the viability of the traditional coding, build and release process. To stay ahead of the curve, development teams are modernizing their approach to dealing with these issues, and as a result are achieving new levels of development productivity. Register for the webcast.
    Date: Wednesday, July 15, 2009
    Time: 11 am PT/2 pm ET
                                   
    INFO-LINK

    Resource Links: