You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为多位置参数应用单一签名测试,简化Raku中Measure类型运算的样板代码

Simplifying Repetitive Measure Operator Multi Subs in Raku

Great question—those repetitive multi subs for every operator definitely add up to unnecessary boilerplate. Let’s look at a couple of clean ways to simplify this, while preserving behavior for both commutative (+, *) and non-commutative (-, /) operators.

Option 1: Merge Real-Measure/Measure-Real Signatures (Per Operator)

For each operator, you can combine the mixed Real/Measure cases into a single multi sub, using a where clause to enforce that at least one argument is a Measure. Then handle parameter order appropriately based on whether the operator is commutative.

Commutative Operators (+, *)

Since order doesn’t matter here, we can safely swap arguments to always work with a Measure first:

multi infix:<+> (Cool:D $a, Cool:D $b) 
    where ($a ~~ Measure:D and $b ~~ Real:D) or ($a ~~ Real:D and $b ~~ Measure:D) 
    is export {
    my ($measure, $real) = $a ~~ Measure:D ?? ($a, $b) !! ($b, $a);
    $measure.clone.add-const($real);
}

multi infix:<+> (Measure:D $left, Measure:D $right) is export {
    my ($result, $argument) = infix-prep($left, $right);
    $result.add($argument);
}

Non-Commutative Operators (-, /)

Here we need to preserve parameter order, so we’ll use a given block to branch based on the argument types:

multi infix:<-> (Cool:D $left, Cool:D $right) 
    where ($left ~~ Measure:D or $right ~~ Measure:D) 
    is export {
    given ($left, $right) {
        when (Measure:D, Real:D) {
            my $result = $left.clone;
            $result.sub-const($right);
        }
        when (Real:D, Measure:D) {
            # Adjust logic to match your Real - Measure requirements
            my $temp = $right.clone.sub-const($left);
            $temp.negate; # Assuming you have a negate method for Measure
        }
        when (Measure:D, Measure:D) {
            my ($result, $argument) = infix-prep($left, $right);
            $result.sub($argument);
        }
    }
}

This cuts down the number of multi subs per operator from 3 to 2, eliminating the duplicate Real-Measure/Measure-Real boilerplate.

Option 2: Use Macros to Generate All Operators (Full Boilerplate Elimination)

If you want to avoid repeating even this per-operator code, Raku’s macros let you generate all four operators in one go. This is perfect if you might add more operators later.

First, define macros for commutative and non-commutative operators:

# Macro for commutative operators (+, *)
macro generate-commutative-op($op, $measure-method, $const-method) {
    quasi {
        multi infix:<$op> (Cool:D $a, Cool:D $b) 
            where ($a ~~ Measure:D or $b ~~ Measure:D) 
            is export {
            if $a ~~ Measure:D and $b ~~ Measure:D {
                my ($result, $argument) = infix-prep($a, $b);
                $result.$measure-method($argument);
            } else {
                my ($measure, $real) = $a ~~ Measure:D ?? ($a, $b) !! ($b, $a);
                $measure.clone.$const-method($real);
            }
        }
    }
}

# Macro for non-commutative operators (- , /)
macro generate-non-commutative-op($op, $measure-method, $left-const-method, $negate-logic) {
    quasi {
        multi infix:<$op> (Cool:D $left, Cool:D $right) 
            where ($left ~~ Measure:D or $right ~~ Measure:D) 
            is export {
            given ($left, $right) {
                when (Measure:D, Real:D) {
                    my $result = $left.clone;
                    $result.$left-const-method($right);
                }
                when (Real:D, Measure:D) {
                    $negate-logic; # Custom logic for Real op Measure
                }
                when (Measure:D, Measure:D) {
                    my ($result, $argument) = infix-prep($left, $right);
                    $result.$measure-method($argument);
                }
            }
        }
    }
}

Then generate all four operators with a single line each:

# Generate + and *
generate-commutative-op('+', 'add', 'add-const');
generate-commutative-op('*', 'mult', 'mult-const'); # Adjust method names to match your code

# Generate - and /
generate-non-commutative-op('-', 'sub', 'sub-const', { $right.clone.sub-const($left).negate });
generate-non-commutative-op('/', 'div', 'div-const', { Measure.new($left) / $right }); # Adjust logic to match your division needs

This approach turns dozens of lines of boilerplate into a handful of macro calls, making your code much easier to maintain.

Key Notes

  • Type Priority: The where clauses ensure these multi subs only trigger when at least one argument is a Measure, so they won’t conflict with Raku’s built-in Real operators.
  • Custom Logic: Adjust the negate-logic or method calls to match exactly how your Measure class handles edge cases like Real - Measure or Real / Measure.

内容的提问来源于stack exchange,提问作者librasteve

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 06:47:47