来自非类型数组的类型化数组

时间:2017-07-28 15:58:59

标签: perl6

假设我们有以下multi sub

multi sub abc(Int        @array) { say 10, ' ', @array; }

multi sub abc(Array[Int] @array) { say 20, ' ', @array; }

multi sub abc(Str        @array) { say 30, ' ', @array; }

multi sub abc(Array[Str] @array) { say 40, ' ', @array; }

正如this question中所提到的,使用类型化数组调用这些可以得到详细信息:

abc Array[Int].new([1,2,3]);

abc Array[Array[Int]].new([Array[Int].new([1,2,3]), Array[Int].new([2,3,4])]);

如果可以从文字中推断出类型,以便我们可以这样做,那就太好了:

abc typed([1,2,3]);

abc typed([[1,2,3],[2,3,4]]);

abc typed(['a', 'b', 'c']);

abc typed([['a', 'b', 'c'], ['b', 'c', 'd']]);

进一步说,让我们添加一个为我们做类型推断的子句:

multi sub abc(@array) { abc typed(@array); }

现在我们可以得到完整的推理而不需要额外强加的语法:

abc [1,2,3];

abc [[1,2,3],[2,3,4]];

abc ['a', 'b', 'c'];

abc [['a', 'b', 'c'], ['b', 'c', 'd']];

以上显示以下内容:

10 [1 2 3]
20 [[1 2 3] [2 3 4]]
30 [a b c]
40 [[a b c] [b c d]]

以下是typed的简单版本,适用于:

  • Array[Int]
  • Array[Array[Int]]
  • Array[Str]
  • Array[Array[Str]]

我的问题是,你将如何实施这种类型的推理?有更好的方法吗?是否已有类似的设施?

sub type-of(\obj)
{
    if obj.^name eq 'Array'
    {
        if obj.map({ type-of($_).^name }).all eq obj.map({ type-of($_).^name })[0]
        {
            my $type = type-of(obj[0]);
            return Array[$type];
        }
        return Array;
    }

    if obj.^name eq 'Int' { return Int; }
    if obj.^name eq 'Str' { return Str; }
}

sub typed(\obj)
{
    if obj.^name eq 'Array'
    {
        return type-of(obj)(obj.List.map({ $_.&typed }).Array);
    }

    return (type-of(obj))(obj);
}

1 个答案:

答案 0 :(得分:1)

  

使用类型化数组调用它们可以获得详细信息

也许你对所有东西的类型约束过度了?

您还可以将该类型存储在常量中,稍微缩短您的代码:

my constant AI = Array[Int];
dd AI.new([1,2,3]);
dd Array[AI].new([AI.new([1,2,3]), AI.new([2,3,4])]);
  

我的问题是,你将如何实施这种类型的推理?

.WHAT pseudo-method返回调用者的类型对象。因此,您可以使用它来获取事物的类型,并且可以使用=:= container identity operator来确定您是否正在处理Array。 (或者,您可以改为使用~~ smartmatch,这样也可以捕获Array的子类。

以下是使用自定义附加运算符的此类使用的示例实现:

sub circumfix:<♥[ ]> (|c) {
    my \array = circumfix:<[ ]>(|c);
    return array unless try array.elems;

    (my $type := array.head.WHAT) =:= Array
      and $type := (array[0] = circumfix:<♥[ ]>(array.head<>)).WHAT;

    my $type-it := True;
    for array.tail: *-1 {
        (my $el-type := .WHAT) =:= Array
          and $el-type := ($_ = circumfix:<♥[ ]>(.<>)).WHAT;
        next if $el-type =:= $type;
        $type-it := False;
    }
    $type-it ?? Array[$type].new: array !! array
}

dd ♥[<1 2 3>];
dd ♥[<a b c>];
dd ♥[[1e0,2], [2,3], [3,3]];
dd ♥[[1,2], [2,3], [3,3]];
dd ♥[[[[1],],],];

# OUTPUT:
# Array[IntStr].new(IntStr.new(1, "1"), IntStr.new(2, "2"), IntStr.new(3, "3"))
# Array[Str].new("a", "b", "c")
# [[1e0, 2], Array[Int].new(2, 3), Array[Int].new(3, 3)]
# Array[Array[Int]].new(Array[Int].new(1, 2), Array[Int].new(2, 3), Array[Int].new(3, 3))
# Array[Array[Array[Array[Int]]]].new(Array[Array[Array[Int]]].new(Array[Array[Int]].new(Array[Int].new(1))))

我们使用|c to capture the args,只需将它们提供给核心[ ]附加内容即可为我们定期Array

然后,我们try看看我们得到的Array是否有任何元素:如果它没有,我们就不会知道如何对它进行参数化和懒惰的事情了#&# 39;让我们找出.elems,然后找出try

此时,我们知道Array中至少有一个元素,因此我们抓住它并使用.WHAT来获取其类型。然后,我们使用=:=检查它是否为Array,如果是,我们只需对其进行递归,存储结果,将$type更新为返回的类型。 <>位只是decont the Array

    (my $type := array.head.WHAT) =:= Array
      and $type := (array[0] = circumfix:<♥[ ]>(array.head<>)).WHAT;

接下来,我们有for循环。我们从第二个元素开始,因为我们在确定要使用的类型时已经处理了第一个元素。

如果任何其他元素与$type的类型不同,我们就无法对Array进行参数化,因此我们只需循环并检查其类型,通过递归来执行相同的业务{ {1}}。如果我们发现任何不同的类型,我们设置一个标记(Arrays),以指示当前$type-it不应该被参数化(我们仍然保持循环,以便我们递归和参数化任何剩余的Array或多个)。

最后,如果设置了标志,我们会使用Array对新的$type进行参数化。否则我们按原样返回Array

简单:)

P.S。:值得注意的是Perl 6支持自引用array,上面的代码会挂起,例如,这个设置:

Arrays

你需要实施一些标记已经看到过的东西; something similar to this(如果您将NQP代码转换为纯Perl 6)