まず、Haskell は厳密ではないため、末尾のこれらの関数呼び出しはまったく評価されない可能性があります。一方、Scala は、戻る前にすべてのリストを計算します。Haskell で起こることに近い実装は次のようになります。
def myFunction[T](l: List[T]): Stream[T] = l match {
case Nil => Stream.empty
case x :: xs => x #:: myFunction(xs)
}
これは厳密なa を受け取り、非厳密なList
a を返します。Stream
ここで、パターン マッチングとエクストラクタを回避したい場合 (ただし、この特定のケースでは何も呼び出されません。以下を参照してください)、次のようにするだけです。
def myFunction[T](xs: List[T]): Stream[T] =
if (xs.isEmpty) Stream.empty else xs.head #:: myFunction(xs.tail)
末尾再帰を使用するつもりであることに気付きました。あなたが書いたものは、再帰の結果x
に追加するため、末尾再帰ではありません。リストを処理するとき、結果を逆方向に計算してから反転すると、末尾再帰が発生します。
def myFunction[T](xs: List[T]): List[T] = {
def recursion(input: List[T], output: List[T]): List[T] = input match {
case x :: xs => recursion(xs, x :: output)
case Nil => output
}
recursion(xs, Nil).reverse
}
最後に、例を逆コンパイルして、それがどのように機能するかを見てみましょう。
class ListExample {
def test(o: Any): Any = o match {
case Nil => Nil
case x :: xs => xs
case _ => null
}
}
生成:
public class ListExample extends java.lang.Object implements scala.ScalaObject{
public ListExample();
Code:
0: aload_0
1: invokespecial #10; //Method java/lang/Object."<init>":()V
4: return
public java.lang.Object test(java.lang.Object);
Code:
0: aload_1
1: astore_2
2: getstatic #18; //Field scala/Nil$.MODULE$:Lscala/Nil$;
5: aload_2
6: invokestatic #24; //Method scala/runtime/BoxesRunTime.equals:(Ljava/lang/Object;Ljava/lang/Object;)Z
9: ifeq 18
12: getstatic #18; //Field scala/Nil$.MODULE$:Lscala/Nil$;
15: goto 38
18: aload_2
19: instanceof #26; //class scala/$colon$colon
22: ifeq 35
25: aload_2
26: checkcast #26; //class scala/$colon$colon
29: invokevirtual #30; //Method scala/$colon$colon.tl$1:()Lscala/List;
32: goto 38
35: aconst_null
36: pop
37: aconst_null
38: areturn
public int $tag() throws java.rmi.RemoteException;
Code:
0: aload_0
1: invokestatic #42; //Method scala/ScalaObject$class.$tag:(Lscala/ScalaObject;)I
4: ireturn
}
デコードすると、最初equals
に渡されたパラメーターとオブジェクトのメソッドが呼び出されますNil
。true の場合は後者を返します。それ以外の場合はinstanceOf[::]
、オブジェクトを呼び出します。true の場合、オブジェクトをそれにキャストし、メソッドを呼び出しますtl
。それがすべて失敗すると、定数をロードしてnull
返します。
つまり、x :: xs
extractor を呼び出していません。
蓄積に関しては、考慮したい別のパターンがあります。
val list = List.fill(100)(scala.util.Random.nextInt)
case class Accumulator(negative: Int = 0, zero: Int = 0, positive: Int = 0)
val accumulator = list.foldLeft(Accumulator())( (acc, n) =>
n match {
case neg if neg < 0 => acc.copy(negative = acc.negative + 1)
case zero if zero == 0 => acc.copy(zero = acc.zero + 1)
case pos if pos > 0 => acc.copy(positive = acc.positive + 1)
})
デフォルトのパラメーターとコピー メソッドは、例を単純にするために使用した Scala 2.8 の機能ですが、要点はfoldLeft
、リストに物を蓄積したい場合にメソッドを使用することです。