Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a 'clamp' method/sub for ranges/Num etc in Raku (i.e. Perl6)?

Tags:

clamp

raku

Is there a 'clamp' or equivalent method or sub in Perl6?

eg

my $range= (1.0 .. 9.9)
my $val=15.3;

my $clamped=$range.clamp($val);
# $clamped would be 9.9

$val= -1.3;
$clamped=$range.clamp($val);
# $clamped would be 1.0
like image 322
drclaw Avatar asked Mar 19 '19 22:03

drclaw


2 Answers

Another tact you might like to explore is using a Proxy, which allows you to define "hooks" when fetching or storing a value from a container

sub limited-num(Range $range) is rw {
    my ($min, $max) = $range.minmax;
    my Numeric $store = $min;
    Proxy.new(
        FETCH => method () { $store },
        STORE => method ($new) {
            $store = max($min, min($max, $new));
        }
    )
}

# Note the use of binding operator `:=`
my $ln := limited-num(1.0 .. 9.9);
say $ln;     # OUTPUT: 1

$ln += 4.2;  
say $ln;     # OUTPUT: 5.2

$ln += 100;  
say $ln;     # OUTPUT: 9.9

$ln -= 50;   
say $ln;     # OUTPUT: 1

$ln = 0;     
say $ln;     # OUTPUT: 1

This particular limited-num will initialise with it's min value, but you can also set it at declaration

my $ln1 := limited-num(1.0 .. 9.9) = 5.5;
say $ln1;    # OUTPUT 5.5;

my $ln2 := limited-num(1.0 .. 9.9) = 1000;
say $ln2;    # OUTPUT 9.9
like image 98
Joshua Avatar answered Nov 23 '22 17:11

Joshua


I don't think so. So, perhaps:

multi clamp ($range, $value) {
  given $range {
    return .max when (($value cmp .max) === More);
    return .min when (($value cmp .min) === Less);
  }
  return $value
} 
my $range = (1.0 .. 9.9);
say $range.&clamp: 15.3; # 9.9
say $range.&clamp: -1.3; # 1

my $range = 'b'..'y';
say $range.&clamp: 'a'; # b
say $range.&clamp: 'z'; # y

The MOP allows direct exploration of the objects available in your P6 system. A particularly handy metamethod is .^methods which works on most built in objects:

say Range.^methods; # (new excludes-min excludes-max infinite is-int ...

By default this includes just the methods defined in the Range class, not the methods it inherits. (To get them all you could use say Range.^methods: :all. That'll net you a much bigger list.)

When I just tried it I found it also included a lot of methods unhelpfully named Method+{is-nodal}.new. So maybe use this instead:

say Range.^methods.grep: * !~~ / 'is-nodal' /;

This netted:

(new excludes-min excludes-max infinite is-int elems iterator
flat reverse first bounds int-bounds fmt ASSIGN-POS roll pick
Capture push append unshift prepend shift pop sum rand in-range
hyper lazy-if lazy item race of is-lazy WHICH Str ACCEPTS perl
Numeric min max BUILDALL)

That's what I used to lead me to my solution above; I sort of know the methods but use .^methods to remind me.


Another way to explore what's available is doc, eg the official doc's Range page. That netted me:

ACCEPTS min excludes-min max excludes-max bounds
infinite is-int int-bounds minmax elems list flat
pick roll sum reverse Capture rand

Comparing these two lists, sorted and bagged, out of curiosity:

say

<ACCEPTS ASSIGN-POS BUILDALL Capture Numeric Str WHICH append
 bounds elems excludes-max excludes-min first flat fmt hyper 
 in-range infinite int-bounds is-int is-lazy item iterator
 lazy lazy-if max min new of perl pick pop prepend push
 race rand reverse roll shift sum unshift>.Bag

 ∩

<ACCEPTS Capture bounds elems excludes-max excludes-min flat
 infinite int-bounds is-int list max min minmax pick
 rand reverse roll sum>.Bag

displays:

Bag(ACCEPTS, Capture, bounds, elems, excludes-max, excludes-min,
flat, infinite, int-bounds, is-int, max, min, pick,
rand, reverse, roll, sum)

So for some reason, list, minmax, and sum are documented as Range methods but are not listed by my .^methods call. Presumably they're called Method+{is-nodal}.new. Hmm.

say Range.^lookup('minmax'); # Method+{is-nodal}.new
say Range.^lookup('minmax').name; # minmax

Yep. Hmm. So I could have written:

say Range.^methods>>.name.sort;

(ACCEPTS ASSIGN-POS AT-POS BUILDALL Bag BagHash Capture EXISTS-POS
 Mix MixHash Numeric Set SetHash Str WHICH append bounds elems
 excludes-max excludes-min first flat fmt hyper in-range infinite
 int-bounds is-int is-lazy item iterator lazy lazy-if list max min
 minmax new of perl pick pop prepend push race rand reverse roll
 shift sum unshift)

Anyhow, hope that's helpful.

like image 30
raiph Avatar answered Nov 23 '22 15:11

raiph