在 Raku(即 Perl6)中是否有针对范围/Num等的“clamp”方法/子例程?

7

Perl6中是否有'clamp'或等效的方法或子程序?

例如:

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

1
哈哈,这是一行噪音代码版本:my ($i,$l,$ll)=(50,1,10); say $i>$l??$l!!$i <$ll??$ll!!$i; - drclaw
3个回答

10

你可能想要尝试另一种方法,即使用代理,它允许你在从容器中获取或存储值时定义“钩子”

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

这个 limited-num 会以它的最小值作为初始值,但你也可以在声明时设置它。

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

6
我不这么认为。因此,也许:
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

MOP(元对象协议)可直接探索您的P6系统中可用的对象。一个特别方便的元方法是.^methods,适用于大多数内置对象:

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

默认情况下,这仅包括Range类中定义的方法,而不包括它继承的方法。(要获取所有方法,可以使用say Range.^methods: :all。这将得到一个更大的列表。)
当我尝试时,我发现它还包括很多不太有用的方法,其名称为Method+{is-nodal}.new。因此,也许最好使用以下代码:
say Range.^methods.grep: * !~~ / 'is-nodal' /;

这实现了以下目标:

(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)

这是我用来引导我到达上述解决方案的方法;虽然我知道这些方法,但使用.^methods提醒自己。
探索可用选项的另一种方式是文档,例如官方文档中的Range页面。这给了我以下结果:
ACCEPTS min excludes-min max excludes-max bounds
infinite is-int int-bounds minmax elems list flat
pick roll sum reverse Capture rand

比较这两个列表,一个是排序的,一个是放入袋子中的,只是出于好奇。
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

显示:

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

对于某些原因,listminmaxsum被记录为Range方法,但它们在我的.^methods调用中未列出。可能它们被称为Method+{is-nodal}.new。嗯。

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

是的。嗯。那么我可以这样写:

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)

无论如何,希望这有所帮助。

谢谢。制作一个子程序很简单,我只是不确定它是否被实现为一些 Perl6 特有的东西。目前我还没有深入研究 MOP,所以还有更多要学习的 ;) - drclaw
为了我的直接目的,我选择了“inline”:$rate= $rate > 0.1??0.1!!$rate; $rate= $rate < -0.1??-0.1!!$rate; - drclaw
1
我认为大多数人使用P6多年,甚至不需要了解MOP,也不会意识到当他们编写say WHAT 42之类的内容时正在使用它。我想起来唯一使用它超出此范畴的事情(还有像HOW这样的)就是.^methods.^lookup - raiph

0
奇怪的是没有人建议使用 augment。诚然,它会创建全局变化,但这可能不是问题。
augment class Range {
   method clamp ($value) { ... }
}

编程相关内容,您需要在augment之前在同一作用域中使用use MONKEY-TYPING。但是这样,您可以简单地说$range.clamp(5)。这比raiph的答案节省了一个字符,但代价是破坏了预编译(不可忽略的成本)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接