2015-05-28 1 views
1

Я изучаю 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.

Так как дир и тело «вызов по имени» параметров. Это означает, что они оцениваются при использовании в функции. Если я это правильно пойму. То, что я не понимаю, как тело

println(i) 
i -= 1 

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

ответ

1

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

println(i) 
i -= 1 

является closure, который работает на переменной 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 и доступ и изменять, когда они оцениваются.

+0

Таким образом, внешнее значение я, который, как представляется, выходит за рамки тела и состояние по-прежнему ссылается внутри тела? Как это технически выполнено? – Juru

+0

@ Juru 'i' находится в окружающей среде как тела, так и состояния. Я обновил ответ, пытаясь сделать функции более явными. Что касается технической реализации закрытий, я рекомендую прочитать [Википедия] (http://en.wikipedia.org/wiki/Closure_ (computer_programming) #Implementation_and_theory) и [этот вопрос на SO] (http://stackoverflow.com/q/11657676/4041697) –

1

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

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

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

Надеюсь, это объяснение даст некоторую помощь.

1

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