2017-07-28 68 views
3

比方說,我們有以下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

調用這些與類型數組可以得到詳細的

也許你過分了一點對一切類型的約束?

您還可以存儲類型在一個常數,縮短你的代碼位:

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)