12

Tôi đã học scala và tôi phải nói rằng đó là một ngôn ngữ thực sự tuyệt vời. Tôi đặc biệt thích khả năng kết hợp mẫu và các chức năng của nó nhưng tôi đến từ một nền tảng javascript, ruby ​​và một trong những mẫu yêu thích của tôi trong các ngôn ngữ đó là mẫu chức năng và phương thức định nghĩa phương thức lười biếng. Một ví dụ trong javascript làđịnh nghĩa chức năng lười biếng trong scala

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

Cùng một mã với các công cụ nhỏ để xác định lại phương thức sau khi tính toán được thực hiện. Loại điều này đến trong thực sự tiện dụng khi tính toán đắt tiền có liên quan và bạn không biết trước thời hạn nếu bạn sẽ cần kết quả. Tôi biết rằng trong scala tôi có thể sử dụng một bộ nhớ cache để mô phỏng cùng một loại kết quả nhưng tôi đang cố gắng tránh kiểm tra có điều kiện và cho đến nay các thử nghiệm của tôi đã trả về kết quả tiêu cực. Có ai biết nếu có một chức năng lười biếng hoặc mô hình định nghĩa phương pháp trong scala?

Lưu ý: Mã javascript là từ số site của Peter Michaux.

+1

Hãy nhớ rằng, trừ khi không thực sự trả lời câu hỏi của bạn, bạn nên đưa ra một trong các câu trả lời cho dấu kiểm màu xanh lá cây đẹp! –

+0

+1 cho liên kết đến bài viết cực kỳ thú vị trên trang web của Peter Michaux. :-) –

Trả lời

25

Tất cả mã phức tạp trong JavaScript đó dường như chỉ cố gắng lưu vào bộ nhớ cache giá trị của ngày. Trong Scala, bạn có thể đạt được điều tương trivially:

lazy val foo = new Date 

Và, nếu thậm chí không muốn thực hiện một val, nhưng muốn gọi một chức năng mà sẽ chỉ thực thi mã tốn kém nếu nó cần nó, bạn có thể

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 

nơi mô hình expensive: => String được gọi là tham số theo tên, mà bạn có thể nghĩ là, "Hãy cho tôi một cái gì đó mà sẽ tạo ra một chuỗi theo yêu cầu." Lưu ý rằng nếu bạn sử dụng nó hai lần, nó sẽ tạo lại nó mỗi lần, đó là nơi mô hình tiện dụng Randall Schultz' do thỏa thuận hợp:

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

Bây giờ bạn tạo ra chỉ khi bạn cần đến nó (thông qua các tham số theo tên) lưu trữ và sử dụng lại nếu bạn cần lại (qua val lười).

Vì vậy, hãy làm theo cách này, không phải là cách JavaScript, mặc dù bạn có thể làm cho Scala trông rất giống với JavaScript.

+1

Tôi chỉ đang cố gắng tìm ra cách thức thành ngữ để đạt được kết quả tương tự trong scala và giống như bạn đã chỉ ra các vals lười biếng và thông số tên là cách để đi vào scala. – davidk01

+0

Phần cuối cùng dường như không chính xác. Tôi đã kiểm tra phần thứ hai và nếu bạn truyền tham số doIt đúng như hàm đắt tiền được gọi mỗi lần. Bạn có thể kiểm tra nó bằng cách chuyển hàm này def đắt = {println ("I got Called"); "return value"} và sau đó kiểm tra nó với (1 đến 3) .foreach (_ => maybeExpensiveTwice (true, đắt tiền)) – Reza

+0

@Reza - Tôi có nghĩa là nó chỉ được gọi một lần gọi phương thức _per mặc dù được sử dụng hai lần trong phương thức đó. Tất nhiên nếu bạn gọi phương thức nhiều lần, nó sẽ được gọi nhiều lần như phương thức được gọi. (Để tránh điều đó bạn cần phải cache nó ở mức cao hơn.) –

19

Scala có lazy val s, người khởi tạo không được đánh giá trừ khi và cho đến khi val được sử dụng. Vals lười biếng có thể được sử dụng như là phương pháp biến địa phương.

Scala cũng có các tham số phương thức theo tên, có các biểu thức tham số thực được gói trong một đoạn và đoạn đó được đánh giá mỗi lần tham số chính thức được tham chiếu trong phần thân phương thức.

Cùng nhau, chúng có thể được sử dụng để đạt được ngữ nghĩa đánh giá lười biếng như là mặc định trong Haskell (ít nhất là trong sự hiểu biết rất hạn chế của tôi về Haskell).

def meth(i: => Int): Something = { 
    //  ^^^^^^ by-name parameter syntax 
    lazy val ii = i 
    // Rest of method uses ii, not i 
} 

Trong phương pháp này, khái niệm sử dụng như là tham số thực tế sẽ được đánh giá một trong hai zero lần (nếu con đường thực hiện năng động của cơ thể phương pháp không bao giờ sử dụng ii) hoặc một lần (nếu nó sử dụng ii một hoặc nhiều lần) .

+0

Cảm ơn bạn đã biết thông tin về các thông số theo tên và vals lười, tôi chắc chắn họ sẽ có ích trong việc đơn giản hóa một số mã của tôi. Đối với câu hỏi ban đầu của tôi nó chỉ ra rằng scala cho phép định nghĩa lại tương tự của các biến chức năng như javascript để gần như cùng một mã hoạt động. Tất cả các thử nghiệm ban đầu của tôi đều có lỗi cú pháp đã ngăn cản tôi không đạt được kết quả tương tự như trong javascript. – davidk01

+2

@ davidk01 - Nhưng bạn không muốn làm điều đó theo cách JavaScript, mặc dù bạn có thể. Có nhiều cách sạch hơn để đạt được điều tương tự trong Scala! –

+0

Khi sử dụng kỹ thuật này, có cách nào để dọn sạch val một cách rõ ràng từ bên ngoài phương pháp không? Tôi tự hỏi mình khi val sẽ được làm sạch bởi GC. Hành vi chung của GC là rõ ràng, nhưng với kỹ thuật nó có thể gây nhầm lẫn, bởi vì phương pháp này có thể được truyền đi và được lưu trữ như một hàm. Tôi đang ở trong một tình huống, nơi mà kỹ thuật này rất hữu ích, nhưng sau một thời điểm nhất định trong thực hiện, tôi muốn làm sạch val, nhưng tôi không muốn dọn dẹp vật thể xung quanh. Bất kỳ cách nào để thực hiện điều này hoặc là điều này chỉ là không thể, bởi vì một val của nó? – user573215

2

Tôi nghĩ ý của bạn là "chức năng lười" là chức năng theo nghĩa đen hoặc ẩn danh.

Trong Scala bạn có thể làm những việc như thế này, rất giống với mã javascript bạn đã đăng.

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

    foo() 
} 

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

Sự khác biệt chính là:

  • Bạn không thể tái phân foo ngoài
  • Không có "chức năng" từ khóa, thay vào đó bạn sử dụng một cái gì đó tương tự (s: String) = > {code}
  • Câu lệnh cuối cùng là giá trị trả về của một khối, do đó bạn không cần thêm "trả lại".
10

Bạn có thể định nghĩa một val lười biếng mà là một chức năng:

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

println(foo()) 

foo() bây giờ sẽ trả lại đối tượng ngày giống nhau mỗi lần, đối tượng này sẽ được khởi tạo foo lần đầu tiên được gọi.

Để giải thích mã một chút, lần đầu tiên foo() được gọi là { val d = new Date;() => { d } } được thực thi, d được gán giá trị ngày mới sau đó nó đánh giá biểu thức cuối () => { d } và gán giá trị foo. Sau đó foo là một hàm không có tham số trả về d.

3

Tôi biết gì về Ruby, nhưng scala có mô hình đối tượng singleton thêm:

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> 

Nếu bạn muốn để có được chức năng, bạn có thể làm cho nó subtype của một loại chức năng:

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

tôi nghĩ rằng một số người trả lời có chút bối rối bởi cách bạn diễn đạt câu hỏi. Scala xây dựng bạn muốn ở đây là một định nghĩa lười biếng đơn giản:

lazy val foo = new java.util.Date 

Việc xây dựng của đối tượng Date sẽ xảy ra cùng một lúc nhất và được hoãn lại cho đến khi tài liệu tham khảo đầu tiên foo.

Các vấn đề liên quan