Skip to content
Snippets Groups Projects
tutorial.wmml 475 KiB
Newer Older
  • Learn to ignore specific revisions
  • <title>Pike tutorial</title>
    
    <!--
    TODO: Add a section about casts
    
    An Appendix about kompiling
    Running Pike on NT
    
    
    sprintf does not work like it used to do since it has been updated
    
    Andreas Lange's avatar
    Andreas Lange committed
    for bignum functionality. 
    The %[n]-feature is documented in the code but not here.
    
    
    Hedda-notes: 
    
    
    Henrik Wallin's avatar
    Henrik Wallin committed
    Reference-part about functions.
    Separate the referece-part from the tutorial?
    Tutorial about how the compiling of Pike-programs and
     interpretation.
    
    
    Fix chapter 3 to be one reference-part and one tutorial-part.
    Make sure that all global functions and modules are described.
    Write an appendix about old functions, behaviors and keywords.
      ("How to convert from old versions of Pike")
    
    
    
    predef::m_delete-behavior is missing. - Fixed by hubbe
    
    Missing funktions in the manual: (tab before function means fixed)
    
    Fredrik Noring's avatar
    Fredrik Noring committed
    has_prefix (is it supposed to be global, or perhaps String.has_prefix? /Noring)
    
    UNDEFINED
    
    ( Stdio.append_path FIXED! /Hedda)
    
    Array.transpose
    
    _debug
    _describe
    _static_modules
    _typeof
        "`!",
        "`!=",
        "`%",
        "`&",
        "`()",
        "`*",
        "`+",
        "`-",
        "`->",
        "`/",
        "`<",
        "`<<",
        "`<=",
        "`==",
        "`>",
        "`>=",
        "`>>",
        "`[]",
        "`^",
        "`|",
        "`~",   (All these should be checked for updates in 5)
    
    	atexit -fixed by hubbe
    
    (chmod fixed /Mirar) 	(Move to system module? -Hubbe)
    
    chroot		need ref
    
    cleargroups
    closelog
    endpwent
    endgrent
    filesystem_stat
    get_all_groups
    get_all_users
    get_groups_for_user
    
    get_weak_flag
    
    getgrent
    getgroups
    
    getegid		need ref
    geteuid		need ref
    getgid		need ref
    
    getgrgid
    getgrnam
    
    gethostbyaddr	need ref
    gethostbyname	need ref
    gethostname	need ref
    
    gethrtime
    gethrvtime
    
    getpgrp	need ref
    
    getppid
    getpwent
    getpwnam
    getpwuid
    
    getuid	need ref
    
    	gmtime - already exists /Hubbe
    
    hardlink
    initgroups
    innetgr
    
    	object_variablep - done /Hubbe
    
    openlog
    readlink
    resolvepath
    set_priority
    set_weak_flag
    setegid
    seteuid
    setgid
    setgrent
    setgroups
    setpwent
    
    setuid		need ref
    
    	sgn - documented /hubbe
    
    symlink
    syslog
    
    this_program (Well, not really a function. Don't know where it belongs. /mast)
    
    	this_thread - doc exists, ptr from global list to module needed?
    	thread_create - doc exists, ptr from global list to module needed?
    	thread_local - doc exists, ptr from global list to module needed?
    
    thread_set_concurrency
    umask
    uname
    
    werror
    
    
    
    These functions shall be removed from chapter 16:
    catch      (Should be a link to misc)
    query_host_name  (Simulate-function! Move!)
    typeof         (Not a function and is documented in one way in misc
                    and another way in 16 (with a typo `?))
    
    
    "This chapter is a reference for all the builtin functions in
     Pike. They are listed in alphabetical order." is not correct if not
    all global functions are included (like threads and Stdio-stuff).
    If the Threads och Stdio-functions begin to work in the modules,
    this comment can be erased.
    
    
    Process.create_process ?  done  -hubbe
    
    Andreas Lange's avatar
    Andreas Lange committed
    #define now handles varargs, should be in chapter 15.
    
    
    <p>
    <center>
    <h1>
    Programming, using and understanding
    <p>
    
    <img src="pike.gif|pike.eps">
    
    <font size=+7>Pike</font>
    
    <p>
    </h1>
    <h2>by Fredrik Hübinette</h2>
    </center>
    
    
    This book was written with the intention of making anybody with a little
    
    programming experience able to use Pike. It should also be possible to
    gain a deep understanding of how Pike works and to some extent why it works
    
    the way it does from this book. It will teach you how to write your own
    
    extensions to Pike. I have been trying for years to get someone else to
    
    write this book, but since it seems impossible without paying a fortune for
    it I will have to do it myself. 
    
    A big thanks goes to
    
    Fredrik Hübinette (Hubbe)'s avatar
    Fredrik Hübinette (Hubbe) committed
    <a href="http://www.emit.com.pl/ian.html">Ian Carr-de Avelon</a> and
    
    <a href="mailto:hedda@idonex.se">Henrik Wallin</a>
    
    Fredrik Hübinette (Hubbe)'s avatar
    Fredrik Hübinette (Hubbe) committed
     for helping
    
    me iron out some of the rough spots. 
    
    
    The book assumes that
    you have programmed some other programming language before and that you
    
    have some experience of UNIX.
    
    <table-of-contents title="Table of contents">
    
    <introduction title="Introduction">
    
    This introduction will give you some background about Pike and this book
    
    and also compare Pike with other languages. If you want to start
    learning Pike immediately you can skip this chapter.
    
    
    This book is designed for people who want to
    
    learn Pike fast. Since Pike is a simple language to learn, especially
    
    if you have some prior programming experience, this should benefit most people.
    <p>
    
    Chapter one is devoted to background information about Pike and this book.
    
    It is not really necessary to read this chapter to learn how to use and
    
    program Pike, but it might help explain why some things work the way they do.
    
    It might be more interesting to re-read the chapter after you have
    
    learned the basics of Pike programming.
    Chapter two is where the action starts. It is a crash course in Pike with
    
    examples and explanations of some of the basics. It explains the
    fundamentals of the Pike data types and control structures.
    
    The systematic documentation of all Pike capabilities starts in chapter three
    with a description of all control structures in Pike. It then continues with
    
    all the data types in chapter four and operators in chapter five. Chapter
    
    six deals with object orientation in Pike, which is slightly different than
    
    what you might be used to.
    
    <!-- FIXME (finish this overview) -->
    
    <anchor name=uLPC>
    <section title="The history of Pike">
    
    In the beginning, there was Zork. Then a bunch of people decided to make
    
    multi-player adventure games. One of those people was Lars Pensjö at the
    
    Per Hedbor's avatar
    Per Hedbor committed
    Chalmers university in Gothenburg, Sweden. For his game he needed a simple,
    
    memory-efficient language, and thus LPC (Lars Pensjö C) was born. About a
    year later I started playing one of these games and found that the language
    was the most easy-to-use language I had ever encountered. I liked the language
    so much that I started improving it and before long I had made my own LPC
    dialect called LPC4. LPC4 was still geared towards writing adventure games,
    but was quite useful for writing other things with as well. A major problem
    with LPC4 was the copyright. Since it was based on Lars Pensjö's code, it
    came with a license that did not allow it to be used for commercial gain.
    
    Fredrik Hübinette (Hubbe)'s avatar
    Fredrik Hübinette (Hubbe) committed
    So, in 1994 I started writing µLPC, which was a new but similar LPC interpreter.
    I got financial backing from Signum Support AB for writing µLPC. Signum
    
    is a company dedicated to supporting GNU and GPL software and they wanted
    to create more GPL software.
    <p>
    
    When µLPC became usable, InformationsVävarna AB started using it for
    
    their web-server. Before then, Roxen (then called Spinner) was non-commercial
    
    and written in LPC4. Then in 1996 I started working for InformationsVävarna
    
    Fredrik Hübinette (Hubbe)'s avatar
    Fredrik Hübinette (Hubbe) committed
    developing µLPC for them. We also changed the name of µLPC to Pike to
    
    get a more commercially viable name.
    
    <section title="A comparison with other languages">
    
    <dl>
    
    <dt> Python
    <dd> Python is probably the language that is most like Pike. Pike is faster
         and has better object orientation. It also has a syntax similar to
         C++, which makes it more familiar for people who know C++. Python on
         the other hand, has a lot more libraries available.
    
    <dt> C++
    
    Martin Stjernholm's avatar
    Martin Stjernholm committed
    <dd> Pike's syntax is almost the same as for C++. A huge difference is that
    
         Pike is interpreted. This makes the code slower, but reduces compile times
         to almost nothing. For those few applications which require the speed of
    
         C or C++, it is often easier to write a Pike extension than to write the
    
         whole thing in C or C++.
    
    <dt> Lisp and Scheme
    
    <dd> Internally Pike has a lot in common with simple interpreted Lisp and
         Scheme implementations. They are all stack based, byte-compiled,
         interpreted languages. Pike is also
    
         a 'one-cell' language, just like Scheme.
    
    <dt> Pascal
    <dd> Pike has nothing in common with Pascal.
    
    <dt> Tcl/Tk
    <dd> Pike is similar to Tcl/Tk in intent and they both have good string
         handling. Pike has better data types and is much faster however.
         On the other hand Tcl/Tk has X windows system support.
    
    <!-- The idea from the beginning was similar but the implementation is very different.-->
    
    <section title="What is Pike">
    
    
    Pike is:
    <ul>
    <li> A programming language
    <li> Object oriented
    <li> Interpreted
    <li> Fast
    <li> Dynamic
    <li> High-level
    <li> similar to C++
    <li> easy to extend
    <li> (a fish)
    </ul>
    
    Pike has:
    <ul>
    <li> Garbage collection
    <li> Advanced string functions
    
    <li> 6 years of development behind it
    
    <li> Advanced data types such as associative arrays
    <li> Support for bignums
    <li> Builtin socket support
    </ul>
    
    </section>
    
    <section title="How to read this manual">
    
    This manual uses a couple of different typefaces to describe different
    things:
    <dl>
    <dt><i>italics</i>
    <dd>Italics is used as a placeholder for other things. If it says <i>a word</i>
        in the text it means that you should put your own word there.
    <dt><b>bold</b>
    <dd>Bold is just used to emphasize that this word is not merely what it sounds
        like. It is actually a <b>term</b>.
    
    <dt><tt>fixed size</tt>
    <dd>Fixed size is used to for examples and text directly from the computer.
    
    Also, please beware that the word <b>program</b> is also a builtin Pike
    
    
    
    <chapter title="Getting started">
    <p>
    
    First you need to have Pike installed on your computer. See <ref to=install>
    
    Fredrik Hübinette (Hubbe)'s avatar
    Fredrik Hübinette (Hubbe) committed
     if this is not already done. It is also vital
    
    for the first of the following examples that the Pike binary is in your UNIX search
    
    path. If you have problems with this, consult the manual for your shell
    or go buy a beginners book about UNIX.
    <p>
    
    <section title="Your first Pike program">
    
    
    int main()
    {
      write("hello world\n");
      return 0;
    }
    
    Let's call this file hello_world.pike, and then we try to run it:
    <p>
    <pre>
    	$ pike hello_world.pike
    	hello world
    	$ 
    </pre>
    Pretty simple, Let's see what everything means:
    
    This begins the function <tt>main</tt>. Before the function name the type of value
    it returns is declared, in this case <tt>int</tt> which is the name of the
    integer number type in Pike. The empty space between the
    
    parenthesis indicates that this function takes no arguments.
    
    A Pike program has to contain at least one function, the <tt>main</tt> function. This function is where program execution starts and thus the function from which every other function is called, directly or indirectly. We can say that this function is called by the operating system.
    Pike is, as many other programming languages, built upon the concept of functions, i.e. what the program does is separated into small portions, or functions, each performing one (perhaps very complex) task. A function declaration consists of certain essential components; the type of the value it will return, the <i>name</i> of the function, the <i>parameters</i>, if any, it takes and the body of the function. A function is also a part of something greater; an object. You can program in Pike without caring about objects, but the programs you write will in fact be objects themselves anyway.
    Now let's examine the body of <tt>main</tt>;
    <p>
    
    {
      write("hello world\n");
      return 0;
    }
    
    Within the function body, programming instructions, statements, are grouped together in blocks. A block is a series of statements placed between curly brackets. Every statement has to end in a semicolon. This group of statements will
    
    be executed every time the function is called.
    <p>
    
    write("hello world\n");
    
    The first statement is a call to the builtin function <tt>write</tt>. This will
    execute the code in the function <tt>write</tt> with the arguments as input data.
    In this case, the constant string <tt>hello world\n</tt> is sent.
    Well, not quite. The <tt>\n</tt> combination corresponds to the newline
    character.
    <tt>write</tt> then writes this string to stdout when executed. Stdout is the standard Unix output channel, usually the screen.
    <p>
    
    This statement exits the function and returns the value zero. Any statements
    following the return statements will not be executed.
    <p>
    </section>
    
    
    <section title="Improving hello_world.pike">
    Typing <tt>pike hello_world.pike</tt> to run our program may seem a bit
    
    unpractical. Fortunately, Unix provides us with a way of automating this
    
    somewhat. If we modify hello_world.pike to look like this:
    <p>
    
    <example language=pike>
    
    #!/usr/local/bin/pike
    
    int main()
    {
      write("hello world\n");
      return 0;
    }
    
    And then we tell UNIX that hello_world.pike is executable so we can run
    
    hello_world.pike without having to bother with running Pike:
    
    <p>
    <pre>
    	$ chmod +x hello_world.pike
    
    	hello world
    	$ 
    </pre>
    N.B.: The hash bang (#!) must be first in the file, not even whitespace is allowed to precede it!
    The file name after the hash bang must also be the complete file name to the Pike binary, and it may not exceed 30 characters.
    <p>
    
    </section>
    
    <section title="Further improvements">
    
    Now, wouldn't it be nice if it said <tt>Hello world!</tt> instead of <tt>hello world</tt> ?
    But of course we don't want to make our program "incompatible" with the old
    version. Someone might need the program to work like it used to. 
    Therefore we'll add a <i>command line option</i> that will make it type the old
    <tt>hello world</tt>. We also have to give the program the ability to choose
    
    what it should output based on the command line option.
    
    This is what it could look like:
    <p>
    
    #!/usr/local/bin/pike
    
    int main(int argc, array(string) argv)
    {
      if(argc &gt; 1 &amp;&amp; argv[1]=="--traditional")
      {
        write("hello world\n"); // old style
      }else{
        write("Hello world!\n"); // new style
      }
      return 0;
    }
    
    Let's run it:
    <p>
    <pre>
    	$ chmod +x hello_world.pike
    	$ ./hello_world.pike
    	Hello world!
    	$ ./hello_world.pike --traditional
    	hello world
    	$ 
    </pre>
    What is new in this version, then?
    
    int main(int argc, array(string) argv)
    
    In this version the space between the parenthesis has been filled.
    
    What it means is that <tt>main</tt> now takes two arguments.
    One is called <tt>argc</tt>, and is of the type <tt>int</tt>.
    The other is called <tt>argv</tt> and is a an array of strings.
    <p>
    The arguments to <tt>main</tt> are taken from the command line when the
    Pike program is executed. The first argument, <tt>argc</tt>, is how many
    words were written on the command line (including the command itself) and
    <tt>argv</tt> is an array formed by these words.
    <p>
    
    if(argc &gt; 1 &amp;&amp; argv[1] == "--traditional")
    {
      write("hello world\n"); // old style
    }else{
      write("Hello world!\n"); // new style
    }
    
    This is an if-else statement, it will execute what's between the first set
    of brackets if the expression between the parenthesis evaluate to something
    other than zero. Otherwise what's between the second set of brackets will
    be executed. Let's look at that expression:
    <p>
    
    argc &gt; 1 &amp;&amp; argv[1] == "--traditional"
    
    Loosely translated, this means: argc is greater than one,  and the second
    element in the array argv is equal to the string <tt>--traditional</tt>. Since
    argc is the number of words on the command line the first part is true only
    
    if there was anything after the program invocation.
    
    <p>
    Also note the comments:
    <p>
    
    write("hello world\n"); // old style
    
    The <tt>//</tt> begins a comment which continues to the end of the line.
    
    Comments will be ignored by the computer when it reads the code.
    
    This allows to inform whoever might read your code (like yourself) of
    
    what the program does to make it easier to understand.
    
    Comments are also allowed to look like C-style comments, i.e. <tt>/* ... */</tt>, which can extend over several lines. The <tt>//</tt> comment only extends to the end of the line.
    
    <p>
    </section>
    
    <section title="Control structures">
    The first thing to understand about Pike is that just like any other
    programming language it executes one piece of code at a time. Most of
    the time it simply executes code line by line working its way downwards.
    
    Just executing a long list of instructions is not enough to make an interesting
    
    program however. Therefore we have <b>control structures</b> to make Pike
    execute pieces of code in more interesting orders than from top to bottom.
    <p>
    We have already seen an example of the <tt>if</tt> statement:
    
    <example language=pike meta=expression,statement1,statement2>
    
    if( expression )
      statement1;
    else
      statement2;
    
    </example>
    
    <!-- FIXME: should if have a capital letter or not? -->
    
    <tt>if</tt> simply evaluates the expression and if the result is true it
    executes <i>statement1</i>, otherwise it executes <i>statement2</i>. If you have no need for
    
    statement2 you can leave out the whole else<!-- FIXME: tt/italics --> part like this:
    
    <example language=pike meta=expression,statement1>
    
    if( expression )
      statement1;
    
    </example>
    
    In this case <i>statement1</i> is evaluated if <i>expression</i> is true, otherwise
    
    nothing is evaluated.
    <p>
    <b>Note for beginners: go back to our first example and make sure you
    
       understand what <tt>if</tt> does.</b>
    
    <p>
    Another very simple control structure is the <tt>while</tt> statement:
    
    <example language=pike meta=expression,statement>
    while( expression )
      statement;
    
    </example>
    
    This statement evaluates <i>expression</i> and if it is found to be true it
    evaluates <i>statement</i>. After that it starts over and evaluates <i>expression</i>
    again. This continues until <i>expression</i> is no longer true. This type of
    control structure is called a <b>loop </b> and is fundamental to all
    
    interesting programming.
    <p>
    </section>
    
    <section title="Functions">
    Another control structure we have already seen is the function.
    A function is simply a block of Pike code that can be executed with different arguments from different places in the program.
    A function is declared like this:
    
    <example language=pike meta=modifiers,type,varname1,varname2,statements>
    
    modifiers type name(type varname1, type varname2, ...)
    {
      statements
    }
    
    </example>
    
    The <i>modifiers</i> are optional. See <ref to=modifiers> for more details about
    
    modifiers. The <i>type </i> specifies what kind of data the function returns.
    For example, the word <tt>int</tt> would signify that the function returns
    an integer number. The <i>name </i> is used to identify the function when
    calling it. The names between the parenthesis are the arguments to the
    
    function. They will be defined as local variables inside the function. Each
    
    variable will be declared to contain values of the preceding type.
    
    The three dots signifies that you can have anything from zero to 256 arguments
    to a function. The <i> statements </i> between the brackets are the function
    body. Those statements will be executed whenever the function is called.
    <p>
    <b>Example:</b>
    
    int sqr(int x) { return x*x; }
    
    This line defines a function called <tt>sqr</tt> to take one argument of the
    
    type <tt>int</tt> and also returns an <tt>int</tt>. The code itself returns
    
    the argument multiplied by itself. To call this function from somewhere in the code
    
    you could simply put: <tt>sqr(17)</tt> and that would return the integer value
    289.
    <p>
    
    As the example above shows, <tt>return</tt> is used to specify the
    
    return value of a function. The value after <tt>return</tt> must be of the
    type specified before the function name. If the function is specified to
    
    Per Hedbor's avatar
    Per Hedbor committed
    return <tt>void</tt>, nothing at all should be written after <tt>return</tt>.
    
    Note that when a return statement is executed, the function will finish
    immediately. Any statements following the return will be ignored.
    <p>
    There are many more control structures, they will all be described in a
    later chapter devoted only to control structures.
    <p>
    </section>
    
    <section title="True and false">
    Throughout this chapter the words <b>true</b> and <b>false</b> have been used
    without any explanation to what they mean. Pike has a fairly simple way of
    looking at this. The number 0 is false and everything else is true.
    
    (Except when using operator overloading as I will explain in a later chapter.)
    
    <p>
    </section>
    
    <section title="Data Types">
    
    As you saw in our first examples we have to indicate the type of value returned by a function or contained in a variable. We used integers (<tt>int</tt>), strings (<tt>string</tt>), and arrays (with the * notation).
    
    The others are <tt>mapping</tt>, <tt>mixed</tt>, <tt>void</tt>, <tt>float</tt>, <tt>multiset</tt>, <tt>function</tt>, <tt>object</tt> and <tt>program</tt>.
    Neither <tt>mixed</tt> nor <tt>void</tt> are really types, <tt>void</tt> signifies that no value should be returned and <tt>mixed</tt> that the return value can be of any type, or that the variable can contain any type of value.
    
    <tt>Function</tt>, <tt>object</tt> and <tt>program</tt> are all types related to object orientation. We will not discuss the last three in any great detail here,
    
    <dd>The integer type stores a signed integer. It is 32 bit or 64 depending
    on architecture.
    
    <dt>Float
    <dd>This variable type stores a floating point number.
    <dt>Array
    
    <dd>Arrays are basically a place to store a number of other values.
    Arrays in Pike are allocated blocks of values.
    They are dynamically allocated and do not need to be declared as in C.
    The values in the array can be set when creating the array like this:
    <example language=pike>
    
    </example>
    Or, if you have already created an array, you can change the values
    in the array like this:
    
    <example language=pike meta=arr,ind,data>
    
    </example>
    This sets entry number <i>ind</i> in the array <i>arr</i> to <i>data</i>.
    <i>ind</i> must be an integer.
    The first index of an array is 0 (zero). A negative index will count from the end of the array rather than from the beginning, -1 being the last element.
    
    To declare that a variable is an array we simply type <tt>array</tt> in
    front of the variable name we want:
    <example language=pike>
    
    </example>
    
    We can also declare several array variables on the same line:
    <example language=pike>
    
    If we want to specify that the variable should hold an array of strings,
    
    we would write:
    <example language=pike>
    
    <dd>A string contains a sequence of characters, a text, i.e. a word, a sentence or a book.
    Note that this is not simply the letters A to Z; special characters, null characters, newlines and so on can all be stored in a string.
    Any 8-bit character is allowed.
    String is a basic type in Pike, it is not an array of char like it is in C.
    This means that you cannot assign new values to individual characters in a string.
    
    Also, all strings are "shared", i.e. if the same string is used in several places, it will be stored in memory only once.
    
    When writing a string in a program, you enclose it in double quotes. To write special characters you need to use the following syntax:
    
    <table small>
    
    <tr><td>\n</td><td>newline</td></tr>
    <tr><td>\r</td><td>carriage return</td></tr>
    <tr><td>\t</td><td>tab</td></tr>
    <tr><td>\b</td><td>backspace</td></tr>
    <tr><td>\"</td><td>" (quotation character)</td></tr>
    <tr><td>\\</td><td>\ (literal backslash)</td></tr>
    
    <dd>A mapping is basically an array that can be indexed on any type, not just integers. It can also be seen as a way of linking data (usually strings) together. It consists of a lot of index-data pairs which are linked together in such a way that map[index1] returns data1.
    
    A mapping can be created in a way similar to arrays:
    <example language=pike>
    
    mapping(string:string) map=(["five":"good", "ten":"excellent"]);
    
    You can also set that data by writing map["five"]="good".
    
    If you try to set an index in a mapping that isn't already present in the mapping it will be added as well.
    <dt>Multiset
    
    <dd>A multiset is basically a mapping without data values. When referring to an index in the multiset a 1 (one) will be returned if the index is present and 0 (zero) otherwise.
    
    </dl>
    
    </section>
    </chapter>
    
    <chapter title="A more elaborate example">
    
    <!-- FIXME: explain things AFTER showing the code? -->
    
    To illustrate several of the fundamental points of Pike we will now
    introduce an example program, that will be extended as we go.
    We will build a database program that keeps track of a record
    collection and the songs on the records. In the first version we
    hard-code our "database" into the program. The database is a mapping
    where the index is the record name and the data is an array of strings.
    The strings are of course the song names. The default register consists
    of one record.
    <example language=pike>
    
    #!/usr/local/bin/pike
    
    mapping (string:array(string)) records =
    ([
      "Star Wars Trilogy" : ({
        "Fox Fanfare",
        "Main Title",
        "Princess Leia's Theme",
        "Here They Come",
        "The Asteroid Field",
        "Yoda's Theme",
        "The Imperial March",
        "Parade of the Ewoks",
        "Luke and Leia",
        "Fight with Tie Fighters",
        "Jabba the Hut",
        "Darth Vader's Death",
        "The Forest Battle",
        "Finale"
      })
    ]);
    
    We want to be able to get a simple list of the records in our database. The function <tt>list_records</tt> just goes through the mapping <tt>records</tt> and puts the indices, i.e. the record names, in an array of strings, record_names. By using the builtin function <tt>sort</tt> we put the record names into the array in alphabetical order which might be a nice touch.
    
    For the printout we just print a header, "Records:", followed by a newline. Then we use the loop control structure <tt>for</tt> to traverse the array and print every item in it, including the number of the record, by counting up from zero to the last item of the array. The builtin function <tt>sizeof</tt> gives the number of items in an array. The printout is formatted through the use of <tt>sprintf</tt> which works more or less like the C function of the same name.
    
    void list_records()
    {
      int i;
      array (string) record_names=sort(indices(records));
    
      write("Records:\n");
      for(i=0;i&lt;sizeof(record_names);i++)
        write(sprintf("%3d: %s\n", i+1, record_names[i]));
    }
    
    If the command line contained a number our program will find the record of that number and print its name along with the songs of this record. First we create the same array of record names as in the previous function, then we find the name of the record whose number (<tt>num</tt>) we gave as an argument to this function. Next we put the songs of this record in the array <tt>songs</tt> and print the record name followed by the songs, each song on a separate line.
    
    void show_record(int num)
    {
      int i;
      array (string) record_names =	sort(indices (records));
      string name=record_names[num-1];
      array (string) songs=records[name];
    
      write(sprintf("Record %d, %s\n",num,name));
      for(i=0;i&lt;sizeof(songs);i++)
        write(sprintf("%3d: %s\n", i+1, songs[i]));
    }
    
    </example>
    The main function doesn't do much; it checks whether there was anything on the command line after the invocation.
    If this is not the case it calls the list_records function, otherwise it sends the given argument to the show_record function.
    When the called function is done the program just quits.
    <example language=pike>
    
    int main(int argc, array (string) argv)
    {
      if(argc &lt;= 1)
      {
        list_records();
      } else {
        show_record((int) argv[1]);
      }
    }
    
    
    <section title="Taking care of input">
    Now, it would be better and more general if we could enter more records into our database. Let's add such a function and modify the <tt>main()</tt> function to accept "commands".
    <p>
    
    <section title="add_record()">
    
    
    Using the method <tt>Stdio.Readline()->read()</tt> we wait for input which will be put into the variable <tt>record_name</tt>. The argument to <tt>->read()</tt> is printed as a prompt in front of the user's input. Readline takes everything up to a newline character.
    
    Now we use the control structure <tt>while</tt> to check whether we should continue inputting songs.
    The <tt>while(1)</tt> means "loop forever", because 1 is always <b>true</b>.
    This program does not in fact loop forever, because it uses <tt>return</tt>
    to exit the function from within the loop when you type a period.
    When something has been read into the variable song it is checked.
    If it is a "." we return a null value that will be used in the while statement to indicate that it is not ok to continue asking for song names.
    If it is not a dot, the string will be added to the array of songs for this record, unless it's an empty string.
    Note the <tt>+=</tt> operator. It is the same as saying
    <tt>records[record_name]=records[record_name]+({song})</tt>.
    
      string record_name=Stdio.Readline()->read("Record name: ");
    
      records[record_name]=({});
      write("Input song names, one per line. End with '.' on its own line.\n");
      while(1)
      {
        string song;
    
        song=Stdio.Readline()->read(sprintf("Song %2d: ",
                                    sizeof(records[record_name])+1));
    
        if(song==".")
           return;
        if (strlen(song))
          records[record_name]+=({song});
      }
    }
    
    
    </section>
    
    <section title="main()">
    
    The main function now does not care about any command line arguments.
    
    Instead we use <tt>Stdio.Readline()->read()</tt> to prompt the user for instructions
    
    and arguments. The available instructions are "add", "list" and "quit".
    
    What you enter into the variables <tt>cmd</tt> and <tt>args</tt> is checked in the
    
    <tt>switch()</tt> block. If you enter something that is not covered
    in any of the case statements the program just silently ignores it and
    asks for a new command.
    
    In a <tt>switch()</tt> the argument (in this case <tt>cmd</tt>) is checked in the <tt>case</tt> statements. The first case where the expression equals <tt>cmd</tt> then executes the statement after the colon. If no expression is equal, we just fall through without any action.
    
    The only command that takes an argument is "list" which works as in the first version of the program.
    If "list" receives an argument, that record is shown along with all the songs
    
    on it. If there is no argument it shows a list of the records in the database.
    
    
     When the program returns from either of the listing functions, the <tt>break</tt> instruction tells the program to jump out of the <tt>switch()</tt> block.
    "add" of course turns control over to the function described above.
    If the command given is "quit" the <tt>exit(0)</tt> statement stops the execution of the program and returns 0 (zero) to the operating system, telling it that everything was ok.
    
    int main(int argc, array(string) argv)
    {
      string cmd;
    
      while(cmd=Stdio.Readline()->read("Command: "))
    
      {
        string args;
        sscanf(cmd,"%s %s",cmd,args);
    
        switch(cmd)
        {
        case "list":
          if((int)args)
          {
            show_record((int)args);
          } else {
            list_records();
          }
          break;
    
        case "quit":
          exit(0);
    
        case "add":
          add_record();
          break;
        }
      }
    }
    
    
    </section>
    </section>
    
    <section title="Communicating with files">
    Now if we want to save the database and also be able to retrieve previously stored data we have to communicate with the environment, i.e. with files on disk.
    
    Now we will introduce you to programming with objects.
    To open a file for reading or writing we will use one of the programs which is builtin in Pike called <tt>Stdio.File</tt>.
    To Pike, a program is a data type which contains code, functions and variables.
    
    A program can be <i>cloned</i> which means that Pike creates a data area
    
     in memory for the program, places a reference to the program in the data area, and initializes it to act on the data in question. The methods (i.e. functions in the object) and variables in the object Stdio.File enable us to perform actions on the associated data file.
    
    The methods we need to use are open, read, write and close. See <ref to=io>
    for more details. <!-- Does this link work? -->
    
    <p>
    
    <section title="save()">
    
    First we clone a <tt>Stdio.File</tt> program to the object <tt>o</tt>.
    
    Then we use it to open the file whose<!-- FIXME: which instead of whose? --> name is given in the string file_name for writing.
    
    We use the fact that if there is an error during opening, open() will return a false value which we can detect and act upon by exiting.
    The arrow operator (-&gt;) is what you use to access methods and variables in an object.
    If there is no error we use yet another control structure, <tt>foreach</tt>, to go through the mapping <tt>records</tt> one record at a time.
    We precede record names with the string "Record: " and song names with "Song: ".
    We also put every entry, be it song or record, on its own line by adding a newline to everything we write to the file.<br>
    
    Finally, remember to close the file.
    
    void save(string file_name)
    {
      string name, song;
      Stdio.File o=Stdio.File();
    
      if(!o-&gt;open(file_name,"wct"))
      {
        write("Failed to open file.\n");
        return;
      }
    
      foreach(indices(records),name)
      {
        o-&gt;write("Record: "+name+"\n");
        foreach(records[name],song)
          o-&gt;write("Song: "+song+"\n");
      }
    
      o-&gt;close();
    }
    
    
    </section>
    
    <section title="load()">
    
    The <tt>load</tt> function begins much the same, except we open the file named <tt>file</tt> for reading instead.
    When receiving data from the file we put it in the string <tt>file_contents</tt>.
    
    The absence of arguments to the method o-&gt;read means that the reading should not end until the end of the file.
    After having closed the file we initialize our database, i.e. the mapping records. Then we have to put <tt>file_contents</tt> into the mapping and we do this by splitting the string on newlines (cf. the split operator in Perl) using the division operator. Yes, that's right: by dividing one string with another we can obtain an array consisting of parts from the first. And by using a <tt>foreach</tt> statement we can take the string <tt>file_contents</tt> apart piece by piece, putting each piece back in its proper place in the mapping records.
    
    void load(string file_name)
    {
      string name="ERROR";
      string file_contents,line;
    
      Stdio.File o=Stdio.File();
      if(!o-&gt;open(file_name,"r"))
      {
        write("Failed to open file.\n");
        return;
      }
    
      file_contents=o-&gt;read();
      o-&gt;close();
    
      records=([]);
    
      foreach(file_contents/"\n",line)
      {
        string cmd, arg;
        if(sscanf(line,"%s: %s",cmd,arg))
        {
          switch(lower_case(cmd))
          {
          case "record":
            name=arg;
            records[name]=({});
            break;
    
           case "song":
             records[name]+=({arg});
             break;
          }
        }
      }
    }
    
    </section>
    
    <section title="main() revisited">
    
    <tt>main()</tt> remains almost unchanged, except for the addition of two case statements with which we now can call the load and save functions. Note that you must provide a filename to load and save, respectively, otherwise they will return an error which will crash the program.
    
    case "save":
      save(args);
      break;
    
    case "load":
      load(args);
      break;
    
    <p>
    </section>
    </section>
    
    <section title="Completing the program">
    Now let's add the last functions we need to make this program useful: the ability to delete entries and search for songs.
    <p>
    
    <section title="delete()">
    
    If you sell one of your records it might be nice to able to delete that entry from the database. The delete function is quite simple.
    First we set up an array of record names (cf. the <tt>list_records</tt> function).
    Then we find the name of the record of the number <tt>num</tt> and use the builtin function <tt>m_delete()</tt> to remove that entry from <tt>records</tt>.
    <example language=pike>
    
    void delete_record(int num)
    {
      array(string) record_names=sort(indices(records));
      string name=record_names[num-1];
    
    </section>
    
    <section title="search()">
    
    Searching for songs is quite easy too. To count the number of hits we declare the variable <tt>hits</tt>. Note that it's not necessary to initialize variables, that is done automatically when the variable is declared if you do not do it explicitly. To be able to use the builtin function <tt>search()</tt>, which searches for the presence of a given string inside another, we put the search string in lowercase and compare it with the lowercase version of every song. The use of <tt>search()</tt> enables us to search for partial song titles as well.
    
    When a match is found it is immediately written to standard output with the record name followed by the name of the song where the search string was found and a newline.
    If there were no hits at all, the function prints out a message saying just that.
    
    void find_song(string title)
    {
      string name, song;
      int hits;
    
      title=lower_case(title);
    
      foreach(indices(records),name)
      {
        foreach(records[name],song)
        {
          if(search(lower_case(song), title) != -1)
          {
            write(name+"; "+song+"\n");
            hits++;
          }
        }
      }
    
      if(!hits) write("Not found.\n");
    }
    
    </section>
    
    <section title="main() again">
    Once again <tt>main()</tt> is left unchanged, except for yet another two case statements used to call the <tt>search()</tt> and <tt>delete</tt> functions, respectively. Note that you must provide an argument to delete or it will not work properly.
    
    case "delete":
      delete_record((int)args);
      break;
    
    case "search":
      find_song(args);
      break;