2012-05-04 3 views
6

В настоящее время я изучаю Scala, работая над книгой «Программирование в Скале». До сих пор были хорошие объяснения всего, что выглядит странно (с точки зрения программиста Java), но это один из примеров использования потока для создания последовательности Фибоначчи оставляет меня вид недоумевают:Каким образом Stream-cons # :: переведен в Scala?

def fibFrom(a: Int, b: Int): Stream[Int] = 
    a #:: fibFrom(b, a + b) 

Как строительство потока? Конечно, оператор #:: как-то ответственен за это. Я понимаю, что, поскольку он заканчивается в :, он является право-ассоциативным, но это не объясняет создание Stream. Я предполагаю, что это неявно переводится конструктору, но я не понимаю, почему и как именно.

Я уже искал ответы в Predef.scala и LowPriorityImplicits.scala, но пока не повезло.

Может ли кто-нибудь просветить меня?

ответ

8

Это право ассоциативный, так что работает в качестве метода по правому аргументу:

fibFrom(b, a + b).#::(a) 

По крайней мере, это то, что он пытается сделать синтаксический. Stream[Int] не имеет такого метода. К счастью, хотя object Stream имеет неявный класс ConsWrapper, у которого есть этот метод (code).

Итак, что вы получите после неявного разрешения заключается в следующем:

immutable.this.Stream.consWrapper(fibFrom(b, a + b)).#::(a) 
+0

Спасибо, все. Я забыл, что неявные преобразования также могут быть определены в сопутствующем объекте класса, который должен быть преобразован. – rolve

+5

Scaladoc должен, вероятно, автоматически включать эти жестко закодированные сопутствующие импликации. – Debilski

+2

Возможно, укажите другим, знакомым с Scala, что это не так просто, что это право-ассоциативный. Кроме того, ConsWrapper реализует '# ::' как [call-by-name] (https://github.com/scala/scala/blob/v2.10.3/src/library/scala/collection/immutable/Stream. # L1042 Ла Скала). т.е. '⇒' в' ConsWrapper (tl: ⇒ Stream [A]) ', так что перевод в' immutable.this.Stream.consWrapper (fibFrom (b, a + b)). #: :(a) 'означает 'fibFrom (b, a + b)' будет когда-либо вызываться только при доступе. – nicerobot

2

потоке похож на список. Он знает только свою голову и остальную часть потока: Стрим (голова: T, хвост: поток [T]). Разница в том, что поток оценивается лениво. ':' В конце имени метода говорит, что метод является правильным ассоциативным. Таким образом, выражение a # :: fibFrom (b, a + b) переводится (компилятором) в fibFrom (b, a + b). #: :(a).

+0

Спасибо, вот что я понял, но он не объясняет, как создается объект Stream. Если Stream имеет метод '# ::', это приведет к бесконечной рекурсии, я думаю. – rolve

+0

# :: такой же как :: в списке. И да, это бесконечная рекурсия. Вы просто берете количество элементов, которые вы хотите. Это то, что я хотел выразить, сказав, что это лениво оценивается. Это означает, что он будет просто вычислен, когда потребуется вычислить. –

+0

Это не совсем то же самое, нет. Если бы это было то же самое, у вас была бы бесконечная рекурсия. Вместо этого '# ::' (определенный в классе 'ConsWrapper', а не Stream!) Имеет параметр by-name, который оценивается лениво. – rolve

Смежные вопросы