12

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

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

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

Примечание: код javascript относится к продукту Peter Michaux's site.

+1

Помните, что если вы действительно не ответили на свой вопрос, вы должны дать один из ответов довольно зеленую галочку! –

+0

+1 для ссылки на чрезвычайно интересную статью на сайте Питера Мишо. :-) –

ответ

25

Все, что сложный код в JavaScript, похоже, просто пытается кэшировать значение даты. В Scala, вы можете достичь того же тривиальным:

lazy val foo = new Date 

И, если даже не хотят, чтобы сделать ВАЛ, но хотите вызвать функцию, которая будет выполнять только дорогой код, если он нуждается в этом, вам может

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 называется по имени параметра, который вы можете думать, как «Дайте мне что-нибудь, что будет генерировать строку по запросу.» Обратите внимание, что если вы используете его в два раза, он будет восстанавливать его каждый раз, который является, где удобно картина Рэндалла Шульца»приходит в:

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.

+1

Я просто пытался понять, что идиоматическим способом было добиться тех же результатов в scala и, как вы указали, ленивые валы и по параметрам имени - это способ пойти в scala. – davidk01

+0

Последняя часть кажется неправильной. Я тестировал вторую часть, и если вы передаете параметр doIt как истинный, дорогая функция вызывается каждый раз. Вы можете проверить это, передав эту функцию def дорого = {println («Я получил Called»); «возвращаемое значение»}, а затем проверить его с помощью (от 1 до 3) .foreach (_ => maybeExpensiveTwice (true, cost)) – Reza

+0

@Reza - Я имею в виду, что он вызван только вызовом метода _per, несмотря на то, что он дважды используется в этом методе. Конечно, если вы вызовете метод несколько раз, он будет вызываться столько раз, сколько вызывается методом. (Чтобы избежать этого, вам нужно кэшировать его на более высоком уровне.) –

19

Scala имеет lazy val s, инициализаторы которого не оцениваются до тех пор, пока не будет использован вал. 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 один или несколько раз) ,

+0

Спасибо за информацию о параметрах by-name и ленивых vals, я уверен, что они пригодится в упрощении моего кода. Что касается моего первоначального вопроса, выясняется, что scala позволяет аналогичное переопределение функциональных переменных как javascript, так что почти тот же код работает. Все мои исходные тесты имели синтаксические ошибки, которые мешали мне достичь тех же результатов, что и в javascript. – davidk01

+2

@ davidk01 - Но вы не хотите делать это способом JavaScript, хотя можете. В Scala есть намного более чистые способы добиться того же! –

+0

При использовании этой техники существует ли способ явно очистить вал извне метода? Я спрашиваю себя, когда вал будет очищен GC. Общее поведение GC ясное, но с техникой это может ввести в заблуждение, потому что этот метод может быть передан и сохранен как функция. Я в ситуации, когда этот метод очень полезен, но после определенного момента выполнения я хотел бы очистить значение, но я не хочу очищать окружающий объект. Любой способ добиться этого или это просто невозможно, потому что его вал? – user573215

2

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

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

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

    foo() 
} 

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

Основное отличие заключается в том, что:

  • Вы не могли бы переназначение внешнего Foo
  • Там нет "функции" ключевого слова, вместо того, чтобы использовать что-то вроде (ы: String) = > {code}
  • Последний оператор - это возвращаемое значение блока, поэтому вам не нужно добавлять «возврат».
10

Вы можете определить ленивый Вал, который является функцией:

lazy val foo = { 
    val d = new Date 
() => { d } 
} 

println(foo()) 

foo() теперь будет возвращать тот же дату объекта каждый раз, объект, который будет инициализирован в первый раз Foo называется.

Чтобы объяснить код немного, первый раз, когда выполняется foo Тогда foo - это функция без параметров, возвращающих d.

3

Я не известно ничего о Ruby, но Скала одноплодного шаблон объекта также:

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

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

lazy val foo = new java.util.Date 

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