Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Dumb, non-question: How to return 'nothing'.

by BrowserUk (Patriarch)
on Jun 17, 2011 at 15:40 UTC ( [id://910184]=perlmeditation: print w/replies, xml ) Need Help??

I want to write an iterator that traverses a data-structure:

my $iter = getIter( $structRef ); while( my $next = $iter->() ) { ## use $next; }

Problem: elements of the data structure can legitimately return undef. And 0. And -1. And ''. And any other value a scalar can hold.

It is an age old problem. As soon as you define [sic] an undefined value -- or null or nil or bottom -- it become useful to be able to store it.

And once you can store it, it is no longer useful for detecting exceptional, non-error conditions.

What is needed is a 'nothing' place-holder.

A non-storable, non-value that if detected in a while or for loop causes the loop to terminate -- without error.

But if you try to assign it to any variable, terminates with an exception.


Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

Replies are listed 'Best First'.
Re: Dumb, non-question: How to return 'nothing'.
by Anonymous Monk on Jun 17, 2011 at 15:53 UTC
    See Higher Order Perl, Chapter 4.5 THE SEMIPREDICATE PROBLEM
    # array reference version sub make_iterator { ... return Iterator { my $return_value; ... if (exhausted) { return; } else { return [$return_value]; } } }

      That's a nice solution, but is there any particular reason to use an arrayref rather than a scalarref? It'd make the iterating code just a bit cleaner:

      while( my $next = $iter->() ) { $next = $$next; ... }

      As opposed to:

      while( my $next = $iter->() ) { ($next) = @$next; ... }

      "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

        I think I've settled on requiring that the iterator be called in a list context. Eg.:

        sub i{ my $n = shift; return sub{ $n ? $n-- : () } };; $i = i( 5 ); print $_ while ( $_ ) = $i->();; 5 4 3 2 1

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
        That's a nice solution, but is there any particular reason to use an arrayref rather than a scalarref?

        Its copy/paste from the book, the first solution -- I guess its more generic, re-usability and all that :)

Re: Dumb, non-question: How to return 'nothing'.
by ikegami (Patriarch) on Jun 17, 2011 at 20:50 UTC

    You could use a list assignment in scalar context, and return an empty list to terminate.

    sub list_iter { my @list = @_; return sub { return @list ? shift(@list) : (); }; } my $iter = list_iter('a', 1, '', 0, undef, -1); while( my ($next) = $iter->() ) { print( $next // '[undef]', "\n" ); }

    Another way is to populate the value using an output parameter.

    sub list_iter { my @list = @_; return sub { return 0 if !@list; $_[0] = shift(@list); return 1; }; } my $iter = list_iter('a', 1, '', 0, undef, -1); while( $iter->( my $next ) ) { print( $next // '[undef]', "\n" ); }

    In both cases, the output is

    a 1 0 [undef] -1

    Update: Added second method. Removed some redundant text to "make space" for second method.

Re: Dumb, non-question: How to return 'nothing'.
by Limbic~Region (Chancellor) on Jun 18, 2011 at 14:57 UTC
    BrowserUk,
    While you already have a solution that works for your specific needs, I have come up with several others in the past:
    while (my $next = $iter->()) { # ... last if $iter->('exhausted'); }
    The above works when your iterator is designed to be called in a void contextwithout parameters. Even when it is not, you can come up some argument that is assumed to never be a valid input - that is of course, until there is a situation like you have where any valid scalar value is valid input, then you might do something like:
    while (my $next = $iter{next}->()) { # ... last if $iter{exhausted}->(); }
    The above may look ugly at first, but it allows possibilities like reversing the direction of the iteration, peeking at the next value without advancing the iterator, going back to the previous value, "redo"ing, etc. I have very seldom had a need for it but there it is.

    Cheers - L~R

Re: Dumb, non-question: How to return 'nothing'.
by JavaFan (Canon) on Jul 03, 2011 at 20:24 UTC
    The problem is that you're overloading the return value of $iter->(). Which comes from using a closure instead of an object.

    I'd say, if your iterating can return anything, including whatever you intent to signal "end of input", you should probably not use a closure. Use an object, and write something like:

    while (!$iter->end_of_data) { my $next = $iter->next; ... }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://910184]
Approved by davies
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (5)
As of 2024-03-28 23:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found