Динамически меняющиеся функции scala

Я изучаю Scala, и я наткнулся на следующий код.

def whileLoop(cond: => Boolean)(body: => Unit): Unit =
    if (cond) {
      body
      whileLoop(cond)(body)
    }
  var i = 10
  whileLoop (i > 0) {
    println(i)
    i -= 1
  }

Выходные числа от 10 до 1.

Таким образом, и cond, и body являются параметрами "call by name". Это означает, что они оцениваются при использовании в функции. Если я правильно понимаю. Я не понимаю, как тело

println(i)
i -= 1

Изменения для каждого уровня рекурсии, который применяется, тело изменяется по мере изменения переменной i. Но как это работает? Каждый раз, когда передается одно и то же тело функции, для меня эта функция остается прежней, но запуск программы показывает мне обратное. Я знаю, что функция оценивается каждый раз, но я не понимаю, как переменная i внутри изменяется каждый раз, поэтому кто-то может объяснить мне, как это работает?

3 ответа

Решение

В этом примере тело

println(i)
i -= 1

это замыкание, которое работает с переменной i который находится в рамках определения тела. следовательно i не является локальной переменной тела, это означает, что операция -= изменяет только существующее значение i, а не локальная копия, которая отбрасывается после вызова метода.

То же самое верно для условия: это замыкание, которое захватывает ту же самую переменную iследовательно, после каждого выполнения тела условие будет видеть обновленное значение i,

Давайте немного перепишем пример без изменения значения: во-первых, мы можем переписать whileLoop принимать функции в качестве аргументов вместо параметров по имени:

def whileLoop(cond: () => Boolean)(body: () => Unit): Unit =
  if (cond()) {
    body()
    whileLoop(cond)(body)
  }

Это переписано whileLoop семантически идентичен, поскольку аргумент call-by-name передается как выражение, а не как оценка выражения. Отказ от ответственности: я не знаю, есть ли технические различия, например, в отношении производительности.

Во-вторых, мы можем сделать выражения, которые передаются для cond а также body функции, которые не имеют аргументов:

val condDef = () => i > 0

val bodyDef = () => {
  println(i)
  i -= 1
}

Поскольку оба они ссылаются на переменную i которые не являются частью их параметров и не определены внутри их тела, мы должны поставить i в их объеме.

def main(args: Array[String]) {
  var i = 10

  val condDef = () => i > 0

  val bodyDef = () => {
    println(i)
    i -= 1
  }

  whileLoop (condDef) {
    bodyDef
  }
}

Так i доступен из обоих, condDef а также bodyDef и доступен и изменен, когда они оценены.

i -= 1 берет переменную i и переназначает ее на значение, уменьшенное на 1. Ваше тело ссылается на одну и ту же переменную i, которая изменяется при каждом вызове тела. Игнорируя всю рекурсию и ваш whileLoop, по сути, он делает это:

var i = 10
println(i) // prints 10
i -= 1
println(i) // prints 9
i -= 1
...
i -= 1
println(i) // prints 1
i -= 1
println(i) // prints 0

Когда вы объявляете параметр с типом => Type вы объявляете этот параметр как анонимную функцию (функцию, которая только возвращает Type без какого-либо участия). Поэтому, когда функция вызывается в первый раз, каждый параметр оценивается для этого конкретного значения i каждый раз. Как body изменяет i значение с каждой итерации, программа будет переоценивать i каждый раз body меняет это.

Я знаю, это звучит сложно, но терпите меня. Посмотрим, что произойдет, когда вы удалите =>,

Если вы удалите => Вы не объявляете анонимные функции для переоценки. Вы определяете параметры, которые невозможно переписать. И так как условие не может быть переоценено каждый раз, у вас будет бесконечный булл.

Я надеюсь, что это объяснение поможет.

Другие вопросы по тегам