2013-06-19 33 views
6

Tôi muốn xác định hàm f có chức năng khác g. Chúng tôi yêu cầu g để nhận n Tăng gấp đôi (đối với một số cố định n) và trả lại một Đôi. Cuộc gọi hàm f(g) phải trả lại giá trị cụ thể của n.Hàm scala chung có đầu vào là hàm số của biến số arity

Ví dụ: f(Math.max) = 2 vì Math.sin có loại (Double, Double) => Doublef(Math.sin) = 1 vì Math.sin có loại Double => Double.

Tôi làm cách nào để xác định f bằng cách sử dụng Generics Scala?

Tôi đã thử một số biểu mẫu mà không thành công. Ví dụ:

def f[A <: Product](g: Product => Double) = {...} 

này không làm việc kể từ khi chúng tôi không thể trích xuất các giá trị của n tại thời gian biên dịch, và không thể hạn chế A chứa chỉ Double giá trị.

+0

Bạn nói rằng 'g' mất một n-tuple, nhưng ví dụ của bạn' Math.max' là một hàm n-ary, không phải là một 'Hàm1' lấy một tuple. Bạn nên làm rõ điều đó. –

+0

Cố định, nắm bắt tốt – tba

Trả lời

2

lẽ là giải pháp đơn giản nhất là sử dụng quá tải như

def f(g:() => Double) = 0; 
def f(g: (Double) => Double) = 1; 
def f(g: (Double, Double) => Double) = 2; 
def f(g: (Double, Double, Double) => Double) = 2; 
// ... 

println(f(Math.pow _)); 
println(f(Math.sin _)); 

(Bạn không thể kiểm tra tham số chức năng/trở lại các loại tại thời gian chạy do gõ tẩy xoá, vì vậy tôi tin rằng bạn có thể 't tạo một hàm đầy đủ generic rằng sẽ đáp ứng yêu cầu của bạn.)

4

Đây là một lý do tốt cho tôi để nhìn vào Shapeless, điều mà tôi luôn muốn làm tại một số điểm :)

$ git clone [email protected]:milessabin/shapeless.git 
... 
$ cd shapeless 

(1)

Shapeless cung cấp một số trừu tượng về tính chất và đặc biệt là biểu diễn dưới dạng danh sách không đồng nhất (HList). Một hàm của tính chất tùy ý có thể được xem là FnHList (một hàm lấy một đối số là HList).

$ sbt shapeless-core/console 
scala> import shapeless._ 
import shapeless._ 

scala> def isFunction[A](fun: A)(implicit fnh: FnHLister[A]) {} 
isFunction: [A](fun: A)(implicit fnh: shapeless.FnHLister[A])Unit 

scala> isFunction(math.sqrt _) 

scala> isFunction(math.random _) 

(2)

Bây giờ chúng ta hãy yêu cầu hàm trả về một Double:

scala> def isFunReturningDouble[A](fun: A)(implicit fnh: FnHLister[A] { type Result = Double }) {} 
isFunReturningDouble: [A](fun: A)(implicit fnh: shapeless.FnHLister[A]{type Result = Double})Unit 

scala> isFunReturningDouble(math.sqrt _) 

scala> isFunReturningDouble(math.signum _) 
<console>:12: error: could not find implicit value for parameter fnh: shapeless.FnHLister[Int => Int]{type Result = Double} 
       isFunReturningDouble(math.signum _) 
           ^

(3)

Lớp LUBConstraint loại có thể chứng kiến ​​các giới hạn trên của các đối số danh sách:

scala> def isValidFun[A, B <: HList](fun: A)(implicit fnh: FnHLister[A] { type Result = Double; type Args = B }, lub: LUBConstraint[B, Double]) {} 
isValidFun: [A, B <: shapeless.HList](fun: A)(implicit fnh: shapeless.FnHLister[A]{type Result = Double; type Args = B}, implicit lub: shapeless.LUBConstraint[B,Double])Unit 

scala> isValidFun(math.random _) 

scala> isValidFun((i: Int) => i.toDouble) 
<console>:12: error: could not find implicit value for parameter lub: shapeless.LUBConstraint[B,Double] 
       isValidFun((i: Int) => i.toDouble) 
         ^

(4)

Bây giờ chúng tôi vẫn cần phải giải nén sự tinh thần bằng cách nào đó. Ở cấp độ loại này, sẽ là Length được cung cấp cho HList. Để có được một giá trị thời gian chạy, một loại lớp học ToInt là cần thiết.

Dưới đây là chức năng cuối cùng:

import shapeless._ 

def doubleFunArity[A, B <: HList, C <: Nat](fun: A)(implicit 
    fnh: FnHLister[A] { type Result = Double; type Args = B }, 
    lub: LUBConstraint[B, Double], 
    len: Length[B] { type Out = C }, 
    res: ToInt[C] 
): Int = res() 

Test:

scala> doubleFunArity(math.sqrt _) 
res15: Int = 1 

scala> doubleFunArity(math.random _) 
res16: Int = 0 

scala> val g: (Double, Double) => Double = math.max _ 
g: (Double, Double) => Double = <function2> 

scala> doubleFunArity(g) 
res17: Int = 2 

Lưu ý rằng tiếc là nhiều math hoạt động đang bị quá tải, và không có loại hạn chế mạnh mẽ, Scala sẽ không cung cấp cho bạn Double phiên bản tự động, nhưng sẽ sử dụng phiên bản Int vì một số lý do:

scala> math.max _ 
res18: (Int, Int) => Int = <function2> 

Vì vậy, tôi cần có hướng dẫn math.max _: ((Double, Double) => Double) để thực hiện công việc này.


Không nói rằng đây là cách tốt nhất để làm điều đó trong trường hợp cụ thể của bạn, nhưng tôi nghĩ đó là một khám phá thú vị.

+0

P.S. 'math.max (_: Double, _: Double)' cũng sẽ làm –

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