Еще одна интересная конструкция в Scala – by-name parameters. Эта конструкция позволяет реализовать отложенные вызовы – до тех пор, пока в теле основной функции не произойдет обращение к функции, переданной как by-name parameter, она вычисляться не будет. Пример: пусть у нас есть некоторая функция, которая проводит расчет и выводит на печать результат неких вычислений, если соответствующий флаг установлен в true. В интерпретаторе Scala набираем следующие строки:
Кстати, тут можно увидеть и замыкание – функция doCalc обращается к переменной enabled, которая не является ее аргументом, но находится в контексте выполнения. Можно попробовать запустить нашу функцию:
scala> doCalc(5+5)
10
Если установить enabled в false, то ничего на печать не выведется:
scala> enabled = false
enabled: Boolean = false
scala> doCalc(5+5)
Ничего на консоль не выведется, как и было задумано. Вроде бы, наш скрипт делает то, что мы от него хотим – если разрешено, печатает, если запрещено – нет. Однако, это не совсем так, в чем легко убедиться, запустив нашу функцию например так (не забыв при этом проверить, что enabled установлен в false):
scala> doCalc(5/0)
java.lang.ArithmeticException: / by zero
at .(:7)
at .()
at RequestResult$.(:3)
at RequestResult$.()
at RequestResult$result()
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAcce…
Как видим, значение передаваемой функции вычисляется уже при передаче ее как параметра, то есть происходит передача по значению вне зависимости от того, будем ли мы вообще обращаться к этой функции. Это нам не подходит, поскольку мы должны вычислять значение функции только тогда, когда происходит собственно обращение к этому параметру. Тут и поможет передача параметров по имени. Модифицируем нашу функцию следующим образом:
def doCalc2(predicate: => Int) = if (enabled) println(predicate)
и выполним предыдущий вызов:
scala> doCalc2(5/0)
В этот раз значение функции не вычислится, потому что к параметру не обращались. Если установить enabled = true и запустить повторно, увидим знакомую картину – произошло обращение к параметру и вычислилось значение функции:
scala> doCalc2(5/0)
java.lang.ArithmeticException: / by zero
at .(:7)
at .()
at RequestResult$.(:3)
at RequestResult$.()
at RequestResult$result()
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAcce…
Таким образом можно реализовывать отложенные(lazy) вызовы. Подробнее можно почитать тут.
Метки: by-name parameters, Scala