首先,Haskell 是非严格的,因此这些尾部的函数调用可能根本不会被评估。另一方面,Scala 将在返回之前计算所有列表。与 Haskell 中发生的情况更接近的实现是:
def myFunction[T](l: List[T]): Stream[T] = l match {
case Nil => Stream.empty
case x :: xs => x #:: myFunction(xs)
}
它接收一个List
严格的 a ,并返回一个Stream
非严格的 a 。
现在,如果你想避免模式匹配和提取器(尽管在这种特殊情况下没有调用 - 见下文),你可以这样做:
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
。如果为真,则返回后者。否则,它会调用instanceOf[::]
对象。如果为真,它将对象转换为该对象,并调用其tl
上的方法。失败了,加载常数null
并返回它。
所以,你看,x :: xs
没有调用任何提取器。
至于累积,您可能需要考虑另一种模式:
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
但当你想在列表中累积内容时,重点是使用该方法。