On Tue, 2003-10-28 at 22:24, Hiroshi Shinohara wrote:
...
> I have another question with generator.
> A few weeks ago I wrote a program to generate column numbers
> like MS Excel (A,B,..,Z,AA,AB,..IV).
> 
> It worked as I expected and made me think
> "a generator can be put as a argument of a procedure".

A generator is just an expression, so you could rephrase the
above as "an expression can be used as an argument to a procedure"
and be equally correct.  Perhaps the behavior is clearer if you
do think of this way.  The expression gets evaluated and produces
a result that is then passed as a parameter in the procedure call.
If the procedure fails, then that expression is backtracked into
by GDE to see if it can produce (generate) another result.  If it
can, that that result is passed as a parameter in the procedure
call.  Similarly, 'every' can be used to force backtracking.

So, in your example below, you're not passing the generator to
the procedure, but rather a single result from evaluating that
generator (expression).  The every is forcing backtracking.

> ####################
> # generator of column numbers like MS Excel (A,B,..X,AA,AB,..,IV)
> ####################
> # gentest4.icn
> procedure main()
>   every s := linegw(right(msperm(&ucase,1 to 2) \256,2) | &null) do write(s)
> end
> 
> ####################
> # permutation          generator
> ####################
> # arg  [1]: s  string   : seed
> #      [2]: m  integer  : size of string
> # value:       string
> # Usage: every ss := msperm(s,m) do ...
> # ("abc",2) -> "aa","ab","ac","ba","bb","bc","ca","cb","cc"
> procedure msperm(s,m)
>   /m := *s
>   if m = 0 then return ""
>   suspend !s || msperm(s,m-1)
> end
> 
> ####################
> # words to one line    generator
> ####################
> # arg [1]: string  (word generator   stopper: &null)
> #     [2]: integer (screen or paper width)
> #     [3]: string  (delimiter)
> # value  : string  (line)
> # Usage  : every s := linegw(gen() | &null,n_limit,s_delim) do ..
> procedure linegw(s_gen,n_limit,s_delim)
>   static s_buf
>   initial s_buf := ""
>   /n_limit := 79
>   /s_delim := " "
> 
>   if \s_gen then {
>     if (*s_buf + *s_delim + *s_gen) > n_limit then {
>       if *s_buf > 0 then {
>         suspend s_buf do s_buf := ""
>       }
>     }
>     s_buf ||:= if *s_buf = 0 then s_gen else s_delim || s_gen
>   }
> 
>   else if *s_buf > 0 then suspend s_buf do s_buf := ""
> 
> end
...
> every s := linegw(right(msperm(&ucase,1 to 2) \256,2) | &null) do write(s)
> This looks like
>   msperm has a generator  1 to 2  as a argument
>   right  has a generator  msperm  as a argument
>   linegw has a generator  right(generator) | &null  as a argument
> 
> How this program works as I expect?

This works because linegw() is written to work when passed pieces of the
output (one label at a time).  Internally, each call to linegw() takes
a single label and adds it to an 'output' string, which is written (and
cleared) whenever it gets long enough.  Since the every is forcing
msperm() to procedure all of its results, you're calling linegw() 257
times to handle the output.  You're also using &null as a flag value
to linegw() can figure out that there are no more calls coming and so
it is time to output the last output line fragment.  (Remember I said
in an earlier post that a procedure can't tell when an argument that
is a generator has no more results?  Well, you've gotten around that
by using the &null flag.)

By the way, here's a slightly different approach - which trades memory
for simpler code:

  procedure main()
    s := ""
    every s ||:= right(msperm(&ucase, 1 to 2)\256, 2) || " "
    outLines(s)
  end

  procedure msperm(s,m)
    /m := *s
    if m = 0 then return ""
    suspend !s || msperm(s,m-1)
  end

  procedure outLines(s, n_limit:78)
    s ? {
        while not pos(0) do {
            write(move(n_limit) | tab(0))
            }
        }
    return
  end

While this particular problem isn't as good a fit, you could also use 
co-expressions (in this example, via a PDCO) to avoid building up
a long string:

  procedure main()
    outLines{right(msperm(&ucase, 1 to 2)\256, 2)}
  end

  procedure msperm(s,m)
    /m := *s
    if m = 0 then return ""
    suspend !s || msperm(s,m-1)
  end

  procedure outLines(L)
    nextLabel := L[1]
    delim     := [EMAIL PROTECTED] | " "
    width     := [EMAIL PROTECTED] | 78

    n := 0
    while label := @nextLabel do {
        w := *label + *delim
        if n+w > width then {
             write()
             n := 0
             }
        writes(label,delim)
        n +:= w
        }
    if n > 0 then write()

    return
  end

This probably isn't much (if any!) cleaner than your original approach.
However, it does remove the requirement of needed an 'end' (&null)
flag.

-- 
Steve Wampler -- [EMAIL PROTECTED]
Quantum materiae materietur marmota monax si marmota
                    monax materiam possit materiari?


-------------------------------------------------------
This SF.net email is sponsored by: SF.net Giveback Program.
Does SourceForge.net help you be more productive?  Does it
help you create better code?   SHARE THE LOVE, and help us help
YOU!  Click Here: http://sourceforge.net/donate/
_______________________________________________
Unicon-group mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/unicon-group

Reply via email to