2015-12-06 49 views
5

我想在scalalist的不同環節pattern match的類型而headtail的:如何模式匹配scala列表的頭部和尾部類型?

class Solution07 extends FlatSpec with ShouldMatchers { 
    "plain recursive flatten" should "flatten a list" in { 
    val list1 = List(List(1, 1), 2, List(3, List(5, 8))) 
    val list1Flattened = List(1, 1, 2, 3, 5, 8) 

    flattenRecur(list1) should be (list1Flattened) 
    } 

    def flattenRecur(ls: List[Any]): List[Int] = ls match { 
    case (head: Int) :: (tail: List[Any]) => head :: flattenRecur(tail) 
    case (head: List[Int]) :: (tail: List[Any]) => head.head :: flattenRecur(head.tail :: tail) 
    case (head: List[Any]) :: (tail: List[Any]) => flattenRecur(head) :: flattenRecur(tail) // non-variable type... on this line. 
    } 
} 

我得到:

Error:(18, 17) non-variable type argument Int in type pattern List[Int] (the underlying of List[Int]) is unchecked since it is eliminated by erasure case (head: List[Int]) :: (tail: List[Any]) => head.head :: flattenRecur(head.tail :: tail) ^

我缺少什麼?怎麼可能對我來說,模式匹配的headtail類型名單?

+1

嘗試用case((headhead:Int)::(headtail:List [Any]))替換case(head:List [Int])::(tail:List [Any])=>' (尾:列表[任何])=>'這將允許你與類型擦除 – ayvango

+0

問題是行'case(head:List [Any])::(tail:List [Any])''你提到了一個不同的線路,是否應該幫助該線路? – Jas

+1

第三選擇與第二選擇衝突。所以你可以調整第二選擇或第三選擇。由於第三種選擇更通用,因此更容易製作第二種更具體的 – ayvango

回答

6

我同意與HList @Andreas解決方案是解決問題的一個很好的例子,但我還是不明白什麼是這個問題:

def flatten(ls: List[_]): List[Int] = ls match { 
    case Nil => Nil 
    case (a: Int) :: tail => a :: flatten(tail) 
    case (a: List[_]) :: tail => flatten(a) ::: flatten(tail) 
    case _ :: tail => flatten(tail) 
    } 

然後:

println(flatten(List(List("one",9,8),3,"str",4,List(true,77,3.2)))) // List(9, 8, 3, 4, 77) 

我在任務中看不到任何類型擦除的問題,因爲您實際上不需要測試擦除的類型。我故意跳過了我的示例中的所有擦除類型 - 以顯示此內容。類型擦除不清除列表元素的類型信息,它會清除只列出通用的,你在我的情況有Any_類型的信息 - 所以你並不需要,在所有。如果我不想錯過某些東西,在你的例子中,類型根本不會被擦除,因爲無論如何你幾乎在任何地方都有Any

+1

你是對的這個觀察:)我應該適應這個答案嗎? –

+1

也許,我只是刪除關於類型擦除的聲明。我認爲使用'HList'的建議是一個很好的選擇,但我不認爲這是唯一的選擇 - 沒有它你可以輕鬆地生活。 – Archeg

+1

我也許應該提一下,我還沒有使用過沒有形狀的,所以我不是權威的決定是否是好的方法 – Archeg

1

由於類型擦除你可以求助於檢查每個元素。

def flattenRecur(ls: List[Any]): List[Int] = ls match { 
    case head :: tail if (head.isInstanceOf[Int]) 
        => head.asInstanceOf[Int] :: flattenRecur(tail) 
    case head :: tail if (head.isInstanceOf[List[Any]]) 
        => flattenRecur(head.asInstanceOf[List[Any]]) ++ 
         flattenRecur(tail) 
    case _ :: tail => flattenRecur(tail) 
    case _ => Nil 
} 

確實出現哪些工作:

scala> flattenRecur(List(List("one",9,8),3,"str",4,List(true,77,3.2))) 
res8: List[Int] = List(9, 8, 3, 4, 77) 

但作爲一般的經驗法則,避免Any通常是值得的。

+0

我希望我可以避免'任何',但是當我定義'val list1 = List(List(1,1),2,List(3,List(5,8)))''那麼它的類型是已經'列出[任何]'可以做任何事情來爲它有不同的類型,這將使​​我進一步從'類型擦除'節省? – Jas

1

你是由對象系統給定的限制擊:

唯一共同的父爲List[Int]IntAny

所以由於該推理系統只能有把握地認爲,你可以返回Any。提出的解決方案@jwvh是可行的,但可能存在運行時異常的危險。

如果你想解決一個類型安全的方式問題的一個選擇可能是使用HList橫七豎八庫https://github.com/milessabin/shapeless的:https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#heterogenous-lists

+1

請問下面的答案可以看看我的例子嗎?我不認爲(2)是真的 - 序列知道內部元素的類型。它不知道只是它的'T',它不需要任務。我同意'HList'是偉大的,他們是如何解決這個可能的選擇之一,但這不是唯一的選擇 – Archeg

+0

爲了您的擴展答案,這是完全正確的。類型擦除不會影響你。 –