Определения ленивых функций в Scala
-
01-10-2019 - |
Вопрос
Я изучал Scala, и я должен сказать, что это действительно крутой язык. Мне особенно нравится его возможности, соответствующие образу и функциональные литералы, но я прихожу из JavaScript, Ruby Background и один из моих любимых узоров на этих языках - это ленивая функция и определение метода. Пример в JavaScript
var foo = function() {
var t = new Date();
foo = function() {
return t;
};
return foo();
};
Этот же код с незначительными настройками работает в Ruby, где вы просто используете объект Singleton для переопределения метода после выполнения вычислений. Такая вещь приходит в самом удобности, когда задействованы дорогих вычислений, и вы не знаете заранее, если вам понадобится результат. Я знаю, что в Scala я могу использовать кэш для имитации такого же результата, но я пытаюсь избежать условных проверок, и до сих пор мои эксперименты вернули отрицательные результаты. Кто-нибудь знает, есть ли ленивая функция или определение метода в Scala?
Примечание. Код JavaScript от Peter Michaux's сайт.
Решение
Все этот сложный код в JavaScript, кажется, просто попробуйте кэшировать значение даты. В Scala вы можете добиться того же тривиально:
lazy val foo = new Date
И, если даже не хотите сделать Val, но хотите вызвать функцию, которая будет выполнять только дорогой код, если это это нужно, вы можете
def maybeExpensive(doIt: Boolean, expensive: => String) {
if (doIt) println(expensive)
}
maybeExpensive(false, (0 to 1000000).toString) // (0 to 1000000).toString is never called!
maybeExpensive(true, (0 to 10).toString) // It is called and used this time
где рисунок expensive: => String
называется параметрами имени, который вы можете думать о том, что «дай мне что-то, что будет генерировать строку по запросу». Обратите внимание, что если вы используете его дважды, он будет регенерировать его каждый раз, который находится в том случае, если Handy Pattern Randall Schultz находится:
def maybeExpensiveTwice(doIt: Boolean, expensive: => String) {
lazy val e = expensive
if (doIt) {
println(e)
println("Wow, that was " + e.length + " characters long!")
}
}
Теперь вы генерируете только в том случае, если вам это нужно (через параметр по имени) а также Храните его и повторно используйте его, если вам нужно это снова (через ленивый валь).
Так что делайте это так, а не хоть JavaScript, даже если вы мог Сделать Scala очень похоже на JavaScript.
Другие советы
Скала есть lazy val
s, чьи инициализаторы не оцениваются, если и до тех пор, пока валь не используется. Ленивые валы могут быть использованы в качестве метода локальных переменных.
Scala также имеет параметры метода по имени, фактические выражения параметров которых завернуты в Thunk, и этот Thunk оценивается каждый раз, когда формальный параметр ссылается в корпусе метода.
Вместе они могут быть использованы для достижения ленивой оценки семантики, таких как значение по умолчанию в Haskell (по крайней мере, в моем очень ограниченном понимании Haskell).
def meth(i: => Int): Something = {
// ^^^^^^ by-name parameter syntax
lazy val ii = i
// Rest of method uses ii, not i
}
В этом методе выражение, используемое в качестве фактического параметра, будет оцениваться либо нулевым временем (если путь динамического выполнения метода тела никогда не использует ii
) или один раз (если он использует ii
один или несколько раз).
Вы можете определить ленивый валь, который является функцией:
lazy val foo = {
val d = new Date
() => { d }
}
println(foo())
foo()
Теперь теперь вернет один и тот же объект даты каждый раз, объект, который будет инициализирован в первый раз, когда вызывается Foo.
Чтобы немного объяснить код, первый раз Foo () называется { val d = new Date; () => { d } }
выполняется, d назначен новое значение даты, то он оценивает последнее выражение () => { d }
и назначьте его значение FOO. Тогда foo - это функция без параметров, которые возвращают d.
Я думаю, что некоторые из респондентов были немного запутаны тем, как вы сформулировали вопрос. Scala Construct, вы хотите, - это простое ленивое определение:
lazy val foo = new java.util.Date
Строительство объекта даты будет происходить не более одного раз и отложить до первой ссылки на FOO.
Я ничего не знал о Ruby, но Scala имеет шаблон объекта Singleton также:
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 вы могли бы сделать такие вещи, как это, очень похоже на код JavaScript, который вы разместили.
val foo = () => {
val t = new Date()
val foo = () => {t}
foo()
}
println ("Hello World:" + foo())
Основное отличие в том, что:
- Вы не могли повторно назначить внешний FOO
- Нет никаких «функциональных» ключевых слов, вместо этого вы используете что-то вроде (S: String) => {код}
- Последнее утверждение - это возвращаемое значение блока, поэтому вам не нужно добавить «возвращение».