В Scala когда нужно указывать лень? - PullRequest
2 голосов
/ 27 апреля 2020

В локальном Scala REPL можно определять потоки и отложенные списки без ключевого слова lazy.

scala> val fibo: LazyList[BigInt] = (0: BigInt) #:: (1: BigInt) #:: fibo.zip(fibo.tail).map { n => n._1 + n._2 }
fibo: LazyList[BigInt] = LazyList(<not computed>)

scala> fibo(100)
res17: BigInt = 354224848179261915075

scala> val prime: LazyList[Int] = 2 #:: LazyList.from(3).filter(i => prime.takeWhile {
     |    j => j * j <= i
     | }.forall {
     |    k => i % k != 0
     | })
prime: LazyList[Int] = LazyList(<not computed>)

scala> prime(100)
res18: Int = 547

Это будет работать почти так же, как с Stream, а также в Scast. ie. Он также должен работать в проекте IntelliJ, верно?

  @Test def testGCDConsecutivePrimeNumbers(): Unit = {
    val prime: LazyList[Int] = 2 #:: LazyList.from(3).filter(i => prime.takeWhile {
      j => j * j <= i
    }.forall {
      k => i % k != 0
    })
    for (n <- 1 to 100) {
      assertEquals(1, gcd(prime(n), prime(n + 1)))
    }
  }

  @Test def testGCDConsecutiveEvenFibonacciNumbers(): Unit = {
    val fibo: LazyList[Int] = 0 #:: 1 #:: fibo.zip(fibo.tail).map(n => n._1 + n._2)
    for (i <- 0 to 42 by 3) {
      assertEquals(2, gcd(fibo(i), fibo(i + 3)))
    }
  }

За исключением случаев, когда это не так.

Ошибка: (21, 67) прямая ссылка распространяется на определение значения prime

val prime: LazyList[Int] = 2 #:: LazyList.from(3).filter(i => prime.takeWhile {

Ошибка: (32, 43) прямая ссылка распространяется на определение значения fibo

val fibo: LazyList[Int] = 0 #:: 1 #:: fibo.zip(fibo.tail).map(n => n._1 + n._2)

Отмечая их как ленивый, вычищаете ошибки.

Я понимаю, что некоторые вещи работают по-разному в REPL, но я не понимаю, почему это может быть одной из таких вещей. И я, вероятно, здесь упускаю некоторые тонкости, но почему пропуск «lazy» в REPL не вызывает ошибку прямой ссылки. В общем, когда нужно явно указывать лень и почему?

1 Ответ

3 голосов
/ 27 апреля 2020

Это не "одна из тех вещей", которые "работают по-разному в REPL", но это из-за них. В ошибочном коде prime и fibo являются локальными переменными. Когда вы определяете их непосредственно в REPL, они являются свойствами анонимного объекта, то есть он генерирует что-то вроде

object Line1 {
  val fibo = ...
}
import Line1.fibo

И если вы посмотрите на спецификацию, ограничение прямой ссылки применяется только для локальных переменных:

Может являться частью определения объекта или класса или может быть локальным для блока ...

Однако существует ограничение на прямые ссылки в блоках

...