类型数组
比方说,我们有以下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);
}
调用这些与类型数组可以得到详细的
也许你过分了一点对一切类型的约束?
您还可以存储类型在一个常数,缩短你的代码位:
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
的子类)。
这里是这样使用的样本实现,使用自定义操作CIRCUMFIX:
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,只是给他们的核心[ ]
CIRCUMFIX,这将使经常Array
我们。
然后,我们try
,看看我们得到了Array
有任何元素:如果它不,我们不知道如何parametarize它和懒惰的东西不会让我们找出.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
我们不能parametarize我们Array
,所以我们只需循环,并检查它们的类型,用递归做同样的业务上Arrays
的不是。如果我们发现任何不同的类型,我们设置一个标志($type-it
),以指示当前的Array
不应该被参数化(我们仍然保持循环,以便我们对任何剩余的Array
s进行递归和参数化)。最后,如果标志被设置,我们使用$type
参数化一个新的Array
;否则我们会按原样返回我们的array
。
Simples :)
PS:这是值得注意的Perl 6的支持自我指涉Arrays
,和上面的代码将与,比如说挂,这种设置:
my @a; @a[0] = @a;
dd ♥[@a];
你需要实施标志着已经看到的事情的东西; something similar to this(如果您将NQP代码转换为纯Perl 6)