Подтвердить что ты не робот

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

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

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

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

Примечание. Код javascript от Peter Michaux сайт.

4b9b3361

Ответ 1

Все, что сложный код в 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 называется параметром by-name, который вы можете представить как "Дайте мне то, что будет генерировать строку по запросу". Обратите внимание, что если вы используете его дважды, он будет регенерировать его каждый раз, в котором находится удобный шаблон Randall Schultz:

def maybeExpensiveTwice(doIt: Boolean, expensive: => String) {
  lazy val e = expensive
  if (doIt) {
    println(e)
    println("Wow, that was " + e.length + " characters long!")
  }
}

Теперь вы создаете, только если вам это нужно (через параметр by-name), и сохраните его и повторно используйте, если вам это нужно (через lazy val).

Так сделайте так, а не JavaScript, хотя вы можете сделать Scala очень похожим на JavaScript.

Ответ 2

Scala имеет lazy val s, инициализаторы которого не вычисляются до тех пор, пока не будет использоваться значение val. Lazy vals могут использоваться как локальные переменные метода.

Scala также имеет параметры метода by-name, чьи фактические выражения параметров завернуты в 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 один или несколько раз).

Ответ 3

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

Ответ 4

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

lazy val foo = new java.util.Date

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

Ответ 5

Я ничего не знал о 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

Ответ 6

Я думаю, что вы имеете в виду "ленивую функцию", это функция буквальная или анонимная функция.

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

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

    foo()
}

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

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

  • Вы не можете повторно назначить внешний foo
  • Нет ключевого слова "function", вместо этого вы используете что-то вроде (s: String) = > {code}
  • Последний оператор - это возвращаемое значение блока, поэтому вам не нужно добавлять "return".