[Scala] By-name parameters

Еще одна интересная конструкция в Scala – by-name parameters. Эта конструкция позволяет реализовать отложенные вызовы – до тех пор, пока в теле основной функции не произойдет обращение к функции, переданной как by-name parameter, она вычисляться не будет. Пример: пусть у нас есть некоторая функция, которая проводит расчет и выводит на печать результат неких вычислений, если соответствующий флаг установлен в true. В интерпретаторе Scala набираем следующие строки:

var enabled = true
def doCalc(predicate: Int) = if (enabled) println(predicate)

Кстати, тут можно увидеть и замыкание – функция 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) вызовы. Подробнее можно почитать тут.

Метки: ,

Добавить комментарий

Fill in your details below or click an icon to log in:

Логотип WordPress.com

You are commenting using your WordPress.com account. Log Out / Изменить )

Фотография Twitter

You are commenting using your Twitter account. Log Out / Изменить )

Фотография Facebook

You are commenting using your Facebook account. Log Out / Изменить )

Connecting to %s


Follow

Get every new post delivered to your Inbox.