Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

How do I restrict a variable to a range of values?

by tphyahoo (Vicar)
on Jul 27, 2005 at 14:57 UTC ( [id://478626]=perlquestion: print w/replies, xml ) Need Help??

tphyahoo has asked for the wisdom of the Perl Monks concerning the following question:

O monks, how do I restrict a variable to a range of values?

I would like to be able to do something like

$self->status('unknown') or $self->status('good') set an object's stat +us, whereas $self->status('ungnown') generates an error. I would like + this also to be available
use strict; use warnings; my $a_or_b; $a_or_b->{a} = 'a'; #should be okay $a_or_b->{b} = 'b'; #should be okay $a_or_b->{c} = 'c'; #should generate an error
I realize I could use fields qw(good bad unknown)and do something like
$status->{good} = '1'; #should be okay $status->{bad} = '1'; #should be okay $a_or_b->{ungnown} = '1'; #should generate an error
However, this doesn't satisfy me because the status should not be allowed to be simultaneously good and bad.

I feel like this could be useful in a range of situations where I want to "program defensively against typos and the like. I have recently started using fields and hash::lock_keys to die when I make an $object->{misspelled} type mistake. This worked so well that my instinct is now to try this on individual variables. Is there a way to do this?

UPDATE: I guess I can do this with something along the lines of $self->set_status('ungnown') and validate against typos with grep like kwaping said. I just thought there might be some sweeter way to do this. But, the answer appears to be, there really isn't, other than *use accessors". So, I guess I will be using accessors...

UPDATE 2: Ha, I knew there was a way -- thanks jediwizard. Jedi presented a way with tied hashes, but what I really wanted was tied scalars, so here's what I'm using:

use strict; use warnings; my($status); tie $status, 'Status'; $status = 'ungnown'; # warning, and value does not get set. $status = 'good'; # works print "$status"; package Status; use Tie::Scalar; use Carp; use base qw(Tie::StdScalar); sub TIESCALAR { my $class = shift; my $status = shift; bless \$status, $class; } sub STORE { my($me, $value) = @_; unless ($value eq 'good'){ warn("invalid status: $value. Status not set"); return undef; } ${$me} = $value; } sub FETCH { my($status) = @_; return ${$status}; }

Replies are listed 'Best First'.
Re: How do I restrict a variable to a range of values?
by JediWizard (Deacon) on Jul 27, 2005 at 15:22 UTC

    It sounds to me like you want Tie::Hash

    my(%hash); tie %hash, 'MyTiedHash'; $hash{a} = 'goodvalue'; # works $hash{b} = 'badvalue'; # error package MyTiedHash; use Tie::Hash; use Carp; use base qw(Tie::StdHash); sub STORE { my($me,$key,$value) = @_; if($value eq 'goodvalue'){ $me->{$key} = $value; }else{ croak("$value not a good value for $key"); } return 1; }

    They say that time changes things, but you actually have to change them yourself.

    —Andy Warhol

      Yep. That's what I wanted. Thanks!
Re: How do I restrict a variable to a range of values?
by ikegami (Patriarch) on Jul 27, 2005 at 15:16 UTC

    There are many ways. Here are three.

    1) Use accessors. This is a great solution is the var is an object.

    sub STATUS_GOOD () { 0 } sub STATUS_BAD () { 1 } sub STATUS_UNKNOWN () { 2 } sub set_status { my ($self, $status) = @_; $self->{status} = STATUS_GOOD if $status eq 'good'; $self->{status} = STATUS_BAD if $status eq 'bad'; $self->{status} = STATUS_UNKOWN if $status eq 'unknown'; require Carp; Carp::croak("Unknown status"); } sub is_good { return shift->{status} == STATUS_GOOD; } sub is_bad { return shift->{status} == STATUS_BAD; } sub is_unknown { return shift->{status} == STATUS_UNKNOWN; }

    2) Use tied variables. They can validate the data being stored into them.

    3) You could use constants instead of strings. Misspellings would result in compilation errors. It's still possible for bad data to be given, but they'd have to do it on purpose.

Re: How do I restrict a variable to a range of values?
by Arunbear (Prior) on Jul 27, 2005 at 17:03 UTC
Re: How do I restrict a variable to a range of values?
by jeffa (Bishop) on Jul 27, 2005 at 18:15 UTC
Re: How do I restrict a variable to a range of values?
by kwaping (Priest) on Jul 27, 2005 at 15:02 UTC
    Let me answer with a question of my own...

    How does the variable know which values are okay?

    That begs the answer, "I tell it." That implies you have a list of acceptable values, which you can also use to test against (probably with grep) later on. Follow?
Re: How do I restrict a variable to a range of values?
by Pied (Monk) on Jul 28, 2005 at 02:59 UTC
    I think that someone needs to write ACME::Ada to implement
    constraint error
    in Perl :)
Re: How do I restrict a variable to a range of values?
by samizdat (Vicar) on Jul 27, 2005 at 15:06 UTC
    convert to decimal character number using ord() and test that against a numeric range?
Re: How do I restrict a variable to a range of values?
by bofh_of_oz (Hermit) on Jul 27, 2005 at 15:17 UTC
    In two words: you don't.

    In many more words: You can't restrict a variable from having any value it gets assigned. You can, however, write a validation block to check for valid values and complain|die|ignore the rest. I believe that's how it's being done in most cases...

    --------------------------------
    An idea is not responsible for the people who believe in it...

      You can't restrict a variable from having any value it gets assigned.

      yes you can, using tie or at a lower level XS magic.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://478626]
Approved by Corion
Front-paged by radiantmatrix
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (7)
As of 2024-04-16 17:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found