Ответ 1
Во-первых, Haskell является нестрогим, поэтому эти вызовы функций на хвосте никогда не могут быть оценены вообще. Scala, с другой стороны, будет вычислять весь список перед возвратом. Более близкая реализация того, что происходит в Haskell, будет следующим:
def myFunction[T](l: List[T]): Stream[T] = l match {
case Nil => Stream.empty
case x :: xs => x #:: myFunction(xs)
}
Получает List
, который является строгим и возвращает 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
. В противном случае загружает cosntant 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
, когда вы хотите скопировать вещи по списку.