Я изучал scala и должен сказать, что это действительно классный язык. Мне особенно нравятся его возможности сопоставления с образцом и функциональные литералы, но я пришел из javascript, фона ruby, и одним из моих любимых шаблонов в этих языках является ленивый шаблон определения функции и метода. Примером в javascript является
var foo = function() {
var t = new Date();
foo = function() {
return t;
};
return foo();
};
Тот же код с незначительными изменениями работает в ruby, где вы просто используете одноэлементный объект для переопределения метода после выполнения вычислений. Подобные вещи очень удобны, когда требуются дорогостоящие вычисления, и вы заранее не знаете, нужен ли вам результат. Я знаю, что в scala я могу использовать кеш для имитации такого же результата, но я стараюсь избегать условных проверок, и пока мои эксперименты дали отрицательные результаты. Кто-нибудь знает, есть ли в scala ленивый шаблон определения функции или метода?
Вы можете определить ленивую val, которая является функцией:
lazy val foo = {
val d = new Date
() => { d }
}
println(foo())
foo ()
теперь будет возвращать один и тот же объект Date каждый раз, объект, который будет инициализирован при первом вызове foo.
Чтобы немного объяснить код, первый раз foo () вызывается {val d = new Date; () => {d}}
выполняется, d присваивается новому значению даты, затем вычисляется последнее выражение () => {d}
и присваивается значение foo. Тогда foo - это функция без параметров, возвращающих d.
Я думаю, то, что вы имеете в виду под "ленивой функцией", является литералом функции или анонимной функцией.
В Scala вы можете делать подобные вещи, очень похожие на код javascript, который вы опубликовали.
val foo = () => {
val t = new Date()
val foo = () => {t}
foo()
}
println ("Hello World:" + foo())
Основное отличие состоит в том, что:
Scala имеет lazy val
, чьи инициализаторы не оцениваются до тех пор, пока не будет использовано val. Lazy vals можно использовать как локальные переменные метода.
В Scala также есть параметры метода по имени, чьи фактические выражения параметров заключены в преобразователь, и этот преобразователь оценивается каждый раз, когда формальный параметр упоминается в теле метода.
Вместе они могут быть использованы для достижения семантики ленивых вычислений, такой как по умолчанию в Haskell (по крайней мере, в моем очень ограниченном понимании Haskell).
def meth(i: => Int): Something = {
// ^^^^^^ by-name parameter syntax
lazy val ii = i
// Rest of method uses ii, not i
}
В этом методе выражение, используемое в качестве фактического параметра, будет оцениваться либо ноль раз (если динамический путь выполнения тела метода никогда не использует ii
), либо один раз (если он использует ii
один или несколько раз).
Я ничего не знаю о Ruby, но scala также имеет шаблон одноэлементного объекта:
Welcome to Scala version 2.8.0.r22634-b20100728020027 (Java HotSpot(TM) Client VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.
scala> object LazyInit {
| val msec = { println("Hi,I'm here!"); System.currentTimeMillis }
| }
defined module LazyInit
scala> System.currentTimeMillis
res0: Long = 1282728315918
scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
Hi,I'm here!
1282728319929 : 1282728319930
scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
1282728322936 : 1282728319930
scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
1282728324490 : 1282728319930
scala>
Если вы хотите получить функцию, вы можете сделать ее подтипом типа функции:
scala> object LazyFun extends (() => Long) {
| val msec = System.currentTimeMillis
| def apply() = msec
| }
defined module LazyFun
scala> System.currentTimeMillis
res2: Long = 1282729169918
scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729190384 : 1282729190384
scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729192972 : 1282729190384
scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729195346 : 1282729190384
Я думаю, что некоторые из респондентов были немного смущены тем, как вы сформулировали вопрос. Конструкция Scala, которую вы хотите здесь, представляет собой простое ленивое определение:
lazy val foo = new java.util.Date
Создание объекта Date произойдет не более одного раза и будет отложено до первой ссылки на foo.