Бесконечный поток:
val ones: Stream[Int] = Stream.cons(1, ones)
Как возможно, чтобы значение использовалось в его собственном объявлении? Кажется, это должно вызвать ошибку компилятора, но это работает.
Бесконечный поток:
val ones: Stream[Int] = Stream.cons(1, ones)
Как возможно, чтобы значение использовалось в его собственном объявлении? Кажется, это должно вызвать ошибку компилятора, но это работает.
Это не всегда рекурсивное определение. Это действительно работает и производит 1:
val a : Int = a + 1
println(a)
переменная a
создается при вводе val a: Int
, поэтому ее можно использовать в определении. Int
по умолчанию инициализируется 0. Класс будет нулевым.
Как указал @Chris, Stream принимает => Stream[A]
, поэтому применяются немного другие правила, но я хотел объяснить общий случай. Идея остается той же, но переменная передается по имени, что делает вычисления рекурсивными. Учитывая, что он передается по имени, он выполняется лениво. Stream вычисляет каждый элемент один за другим, поэтому он вызывает ones
каждый раз, когда ему нужен следующий элемент, в результате чего один и тот же элемент создается еще раз. Это работает:
val ones: Stream[Int] = Stream.cons(1, ones)
println((ones take 10).toList) // List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
Хотя вы можете упростить бесконечный поток: Stream.continually(1)
Обновить Как @SethTisue указал в комментариях, Stream.continually
и Stream.cons
— это два совершенно разных подхода с очень разными результатами, потому что cons
принимает A
, когда continually
принимает =>A
, что означает, что continually
пересчитывает каждый время элемента и сохраняет его в памяти, когда cons
может избежать его сохранения n раз, если вы не преобразуете его в другую структуру, например List
. Вы должны использовать continually
только в том случае, если вам нужно сгенерировать разные значения. Подробности и примеры см. в комментарии @SethTisue.
Но обратите внимание, что вам необходимо указать тип, как и в случае с рекурсивными функциями.
И вы можете сделать первый пример рекурсивным:
lazy val b: Int = b + 1
println(b)
Это приведет к переполнению стека.
Stream.cons(1, ones)
— это циклическая структура, использующая конечную память, а Stream.continually(1)
— линейная структура, использующая потенциально неограниченную память.
- person Seth Tisue; 05.11.2015
Stream.continually(1).take(10000000)
, все равно не будут выделены все эти номера
- person Archeg; 05.11.2015
take
, вы получаете одну и ту же структуру в любом случае, поэтому важно ли это, зависит от того, что вы на самом деле делаете с потоком.) Дополнительная литература: gist.github.com/SethTisue/ce598578874accba98c0, groups.google.com/d/msg/scala-user/3yypUKJBP04/Q_bowgIry44J
- person Seth Tisue; 05.11.2015
cons
берет A
, когда continually
берет =>A
. Я согласен, что это делает эти два метода совершенно разными.
- person Archeg; 05.11.2015
Посмотрите на подпись Stream.cons.apply а>:
apply[A](hd: A, tl: ⇒ Stream[A]): Cons[A]
⇒
во втором параметре указывает, что он имеет семантику вызова по имени. Поэтому ваше выражение Stream.cons(1, ones)
строго не оценивается; аргумент ones
не нужно вычислять до того, как он будет передан в качестве аргумента для tl
.
Причина, по которой это не приводит к ошибке компилятора, заключается в том, что и Stream.cons
, и Cons
являются нестрогими и лениво оценивает второй параметр.
ones
можно использовать в своем собственном определении, потому что у объекта cons есть метод применения, определенный следующим образом:
/** A stream consisting of a given first element and remaining elements
* @param hd The first element of the result stream
* @param tl The remaining elements of the result stream
*/
def apply[A](hd: A, tl: => Stream[A]) = new Cons(hd, tl)
А Cons определяется так:
final class Cons[+A](hd: A, tl: => Stream[A]) extends Stream[A]
Обратите внимание, что второй параметр tl
передается по имени (=> Stream[A]
), а не по значению. Другими словами, параметр tl
не оценивается, пока он не используется в функции.
Одним из преимуществ использования этого метода является то, что вы можете составлять сложные выражения, которые могут быть вычислены лишь частично.
ones
— это функция без аргументов. - person n. 1.8e9-where's-my-share m.   schedule 05.11.2015