2012-03-20 54 views
13

當我建立使用foldLeft列表我經常生氣在不必明確鍵入注入參數,並希望我可以只使用'無」來代替 - 這裏是一個人爲的例子:爲什麼不通過零來摺疊工作?

scala> List(1,2,3).foldLeft(List[Int]())((x,y) => y :: x) 
res17: List[Int] = List(3, 2, 1) 

scala> List(1,2,3).foldLeft(Nil)((x, y) => y :: x) 
<console>:10: error: type mismatch; 
found : List[Int] 
required: scala.collection.immutable.Nil.type 
       List(1,2,3).foldLeft(Nil)((x,y) => y :: x) 

這並沒有那麼糟與List[Int],但只要你開始使用你自己的類的列表,它幾乎肯定會有更長的名字,甚至是元組或其他容器的列表,所以你需要指定多個類名,這是非常可怕的:

list.foldLeft(List.empty[(SomethingClass, SomethingElseClass)]) { (x,y) => y :: x } 

我猜測re儘管它不起作用,但是如果使用類似5 :: Nil的東西,編譯器可以推斷出空列表的類型爲List[Int],但是當Nil作爲參數傳遞給foldLeft時,它沒有足夠的信息來這樣做,並且它被設置爲使用其類型的時間。但是 - 它真的不可以嗎?它不能推斷作爲第二個參數傳遞函數的返回類型的類型嗎?

如果不是,是否有一些我不知道的整潔的成語?

+1

令人遺憾的是,Scala無法推斷出這類情況的類型。希望今後能對此進行修改。 – 2012-03-20 14:01:05

+0

在實踐中應該不是太多問題。如果你只是處理一些特定的類,可以在頂部輸入一個類型別名,類型爲S =(SomethingClass,SomethingElseClass)或val nil = List.empty [(SomethingClass,SomethingElseClass)]。如果你正在編寫一個通用的方法,你的類型將已經有了簡短的別名,比如'T'和'U'。 – 2012-03-20 17:17:32

回答

14

斯卡拉類型推理引擎從左到右工作 - 因此scalac無法推斷foldLeft的第一個參數列表的正確類型。你必須給編譯器一個提示什麼類型的使用。而不是使用List[TYPE](),你可以用List.empty[TYPE]

(List(1,2,3) foldLeft List.empty[Int]) { (x,y) => y :: x } 
(List.empty[Int] /: List(1, 2, 3)) { (x,y) => y :: x } 
+0

那麼這個問題沒有簡短的解決方法?當它是例如一個元組列表時,它開始顯得荒謬:'list.foldLeft(List.empty [(SomethingClass,SomethingElseClass)]){(x,y)=> y :: x}''。它佔據了整條線,模糊了你實際想要做的事情的意義! – Russell 2012-03-20 11:29:05

+3

@Russell:不,沒有更短的路。你可以做的唯一事情就是定義一個類型別名,當你有多個「long」類型時,這個類型是有用的。 – sschaef 2012-03-20 11:52:02

+0

謝謝你的回答,既然我不能接受,我會接受一個,並提出另一個。 – Russell 2012-03-20 14:33:40

12

Scala的類型推斷的作品一個參數塊在同一時間。沒有任何其他背景的Nil沒有特定類型,因此可以選擇最具限制性的類型(Nothing)。

它也不能推斷函數的返回類型,因爲返回類型取決於Nil的類型。一般來說,擺脫這種循環是非常棘手的(如果你不指定你的意思)。

儘管如此,還是有一些技巧可以使事情變得不那麼麻煩。

首先,fold的類型簽名只有集合類型,所以如果您可以使用這種摺疊類型,則可以解決該問題。例如,您可以編寫自己的反壓平:

List(List(1),List(2),List(3)).fold(Nil)((x,y) => y ::: x) 

其次,如果要創建同一類型的新的集合,它往往更容易使用現有的集合,產生空單,而不是試圖將類型插入空集合中。這是特別容易,如果你定義了管道的地方:

class PipeAnything[A](a: A) { def |>[B](f: A => B) = f(a) } 
implicit def anything_can_be_piped[A](a: A) = new PipeAnything(a) 

List(1,2,3) |> { x => x.foldLeft(x.take(0))((y,z) => z :: y) } 

最後,不要忘記,你可以很容易地可以做一些與你的類型一致定義你自己的方法,即使你使用了一點弄虛作假得到它的工作:

def foldMe[A,B](example: A, list: List[B])(f: (List[A],B) => List[A]) = { 
    (List(example).take(0) /: list)(f) 
} 

scala> foldMe(("",0), List("fish","wish","dish"))((x,y) => (y.take(1), y.length) :: x) 
res40: List[(java.lang.String, Int)] = List((d,4), (w,4), (f,4)) 

這裏,請注意這個例子的不是零,而是僅作爲與您想要的類型的東西的典範。

+0

謝謝你的回答,既然我不能接受,我會接受一個,並提出另一個。 – Russell 2012-03-20 14:34:00