2012-03-20 3 views
13

私はfoldLeftを使用してリストを構築する場合、私は多くの場合、明示的に注入されたパラメータを入力することでイライラして、私はちょうど代わりに `ナシ」を使うことがしたい - ここでの不自然な例です:Nilをfold leftに渡すのはなぜですか?

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 } 

私は、それがうまくいかないのは、5 :: Nilのようなものでは、コンパイラは空のリストの型をList[Int]と推測できますが、にパラメータとしてNilが渡されたときには、その型が使用されるまでの時間が設定されます。しかし、それができないことは本当に本当ですか? 2番目の引数として渡された関数の戻り値の型から型を推論することはできませんか?

もしそうでなければ、ちょっとわかりません。

+1

Scalaがこの種の状況の型を推論できないのは悲しいことです。これが将来改正されることを祈りましょう。 –

+0

実際にはあまり問題にならないはずです。あなたが特定のクラスを扱っているだけの場合は、タイプ 'S =(SomethingClass、SomethingElseClass)'または 'val nil = List.empty((SomethingClass、SomethingElseClass) ')の型エイリアスを作ります。ジェネリックメソッドを書いているなら、型は既に 'T'や' U'のような短いエイリアスを持っています。 –

答えて

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

+3

@Russell:いいえ、もっと短い方法はありません。あなたが行うことができる唯一のことは、複数の "long"型がある場合に便利な型エイリアスを定義することです。 – sschaef

+0

お返事いただきありがとうございます。私は両方を受け入れることができないので、私は1つを受け入れ、もう1つはupvoteします。 – Russell

12

Scalaの型推論は、一度に1つのパラメータブロックを動作します。他のコンテキストを持たないNilには特定のタイプがないため、最も制限のあるタイプ(Nothing)が選択されます。

戻り値の型はNilの型に依存するため、関数の戻り値の型も推定できません。そのような円形度から抜け出すのは一般的に難しい(あなたが何を意味するのかを指定しなければ)。

しかし、煩雑さを軽減するために、適用することができるいくつかの秘訣があります。

最初に、foldのタイプシグネチャにはコレクションの種類のみが含まれているため、そのような折り畳みを使用することができれば、問題を回避することができます。たとえば、あなたがあなた自身の書くことができ、逆に、フラット化:

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

第二に、あなたは同じタイプの新しいコレクションを作成している場合、それはしようとするよりも、空の1を生成するために、既存のコレクションを使用することがしばしば簡単です空のコレクションの型を差し込みます。あなたがどこかに定義されたパイプを持っている場合は特に簡単です:

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

お返事ありがとうございます。私は両方を受け入れることができないので、私は1つを受け入れ、もう1つはupvoteします。 – Russell

関連する問題