假设我们有以下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);
}
答案 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)