ленивые определения функций в scala

Я изучал scala и должен сказать, что это действительно классный язык. Мне особенно нравятся его возможности сопоставления с образцом и функциональные литералы, но я пришел из javascript, фона ruby, и одним из моих любимых шаблонов в этих языках является ленивый шаблон определения функции и метода. Примером в javascript является

var foo = function() {
  var t = new Date();
  foo = function() {
    return t;
  };
  return foo();
};

Тот же код с незначительными изменениями работает в ruby, где вы просто используете одноэлементный объект для переопределения метода после выполнения вычислений. Подобные вещи очень удобны, когда требуются дорогостоящие вычисления, и вы заранее не знаете, нужен ли вам результат. Я знаю, что в scala я могу использовать кеш для имитации такого же результата, но я стараюсь избегать условных проверок, и пока мои эксперименты дали отрицательные результаты. Кто-нибудь знает, есть ли в scala ленивый шаблон определения функции или метода?

Примечание: код javascript взят с сайта Питера Мишо .

14
задан davidk01 25 August 2010 в 03:09
поделиться

5 ответов

Вы можете определить ленивую 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.

10
ответ дан 1 December 2019 в 05:53
поделиться

Я думаю, то, что вы имеете в виду под "ленивой функцией", является литералом функции или анонимной функцией.

В Scala вы можете делать подобные вещи, очень похожие на код javascript, который вы опубликовали.

val foo = () => {
    val t = new Date()
    val foo = () => {t}

    foo()
}

println ("Hello World:" + foo())

Основное отличие состоит в том, что:

  • Вы не можете переназначить внешний foo
  • Ключевое слово function отсутствует, вместо этого вы используете что-то вроде (s:String) => {code}
  • Последний оператор — это возвращаемое значение блока, поэтому вам не нужно добавлять «возврат».
2
ответ дан 1 December 2019 в 05:53
поделиться

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 один или несколько раз).

19
ответ дан 1 December 2019 в 05:53
поделиться

Я ничего не знаю о 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
3
ответ дан 1 December 2019 в 05:53
поделиться

Я думаю, что некоторые из респондентов были немного смущены тем, как вы сформулировали вопрос. Конструкция Scala, которую вы хотите здесь, представляет собой простое ленивое определение:

lazy val foo = new java.util.Date

Создание объекта Date произойдет не более одного раза и будет отложено до первой ссылки на foo.

6
ответ дан 1 December 2019 в 05:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: