2009-01-27 34 views
24

Hình vuông của cạnh huyền của tam giác vuông bằng tổng của các ô vuông ở hai bên kia.Làm thế nào để viết định lý Pythagoras trong Scala?

Đây là định lý Pythagoras. Một hàm để tính toán hypotenuse dựa trên độ dài "a" và "b" của các cạnh của nó sẽ trả về sqrt (a * a + b * b).

Câu hỏi là, làm cách nào bạn xác định một hàm như vậy trong Scala theo cách mà nó có thể được sử dụng với bất kỳ loại nào triển khai các phương thức thích hợp?

Đối với ngữ cảnh, hãy tưởng tượng toàn bộ thư viện các định lý toán học bạn muốn sử dụng với các loại Int, Double, Int-Rational, Double-Rational, BigInt hoặc BigInt-Rational tùy thuộc vào những gì bạn đang làm và tốc độ, độ chính xác , độ chính xác và yêu cầu phạm vi.

+2

Và bây giờ tôi cuối cùng đã biết lý do tại sao các loại cấu trúc sẽ không cho phép tôi làm điều đó: http://article.gmane.org/gmane.comp.lang.scala/7013 –

Trả lời

24

này chỉ hoạt động trên Scala 2.8, nhưng nó làm việc: nói

scala> def pythagoras[T](a: T, b: T, sqrt: T => T)(implicit n: Numeric[T]) = { 
    | import n.mkNumericOps 
    | sqrt(a*a + b*b) 
    | } 
pythagoras: [T](a: T,b: T,sqrt: (T) => T)(implicit n: Numeric[T])T 

scala> def intSqrt(n: Int) = Math.sqrt(n).toInt 
intSqrt: (n: Int)Int 

scala> pythagoras(3,4, intSqrt) 
res0: Int = 5 

Tổng quát hơn, các đặc điểm Numeric là một cách hiệu quả một tài liệu tham khảo về cách giải quyết vấn đề kiểu này. Xem thêm Ordering.

+0

Bạn có thể cập nhật câu trả lời phổ biến này để sử dụng giới hạn ngữ cảnh, giờ đây những điều đó tồn tại. –

+0

@BrianMcCutchon Tôi tin rằng giới hạn ngữ cảnh là thực tế có sẵn trên 2,8, nhưng sau đó tôi cần phải gán chúng cho một biến anyway để tôi có thể nhập 'mkNumericOps' từ nó. Tuy nhiên, có một giải pháp tốt hơn - mặc dù, nếu bạn muốn, hãy hỏi trong một câu hỏi riêng. –

+1

Bạn đang đề cập đến 'nhập Numeric.Implicits._'? Điều đó sẽ cho phép bạn sử dụng giới hạn ngữ cảnh mà không có biến chứng cứ. –

18

Cách rõ ràng nhất:

type Num = { 
    def +(a: Num): Num 
    def *(a: Num): Num 
} 

def pyth[A <: Num](a: A, b: A)(sqrt: A=>A) = sqrt(a * a + b * b) 

// usage 
pyth(3, 4)(Math.sqrt) 

Đây là khủng khiếp vì nhiều lý do. Đầu tiên, chúng ta có vấn đề về kiểu đệ quy, Num. Điều này chỉ được phép nếu bạn biên dịch mã này với tùy chọn -Xrecursive được đặt thành một số giá trị số nguyên (5 có thể là quá đủ cho số). Thứ hai, loại Num là cấu trúc, có nghĩa là bất kỳ việc sử dụng nào các thành viên mà nó định nghĩa sẽ được biên dịch thành các lời gọi phản chiếu tương ứng. Đặt nhẹ nhàng, phiên bản pyth này không hiệu quả một cách tối đa, chạy theo thứ tự của một số trăm nghìn lần chậm hơn so với triển khai thông thường. Không có cách nào xung quanh loại cấu trúc mặc dù nếu bạn muốn xác định pyth cho bất kỳ loại nào xác định +, * và có chức năng sqrt.

Cuối cùng, chúng tôi gặp vấn đề cơ bản nhất: vấn đề quá phức tạp. Tại sao phải thực hiện chức năng theo cách này? Thực tế, các loại duy nhất mà nó cần phải áp dụng là số Scala thực. Do đó, đơn giản nhất là chỉ cần làm như sau:

def pyth(a: Double, b: Double) = Math.sqrt(a * a + b * b) 

Tất cả các vấn đề được giải quyết! Hàm này có thể sử dụng được trên các giá trị của loại Double, Int, Float, thậm chí là số lẻ như Short nhờ vào sự tuyệt vời của chuyển đổi tiềm ẩn. Mặc dù đúng là chức năng này ít linh hoạt về mặt kỹ thuật so với phiên bản có cấu trúc của chúng tôi, nó là bao la hiệu quả hơn và dễ đọc hơn. Chúng tôi có thể đã mất khả năng tính toán định lý Pythagrean cho các loại không lường trước được xác định +*, nhưng tôi không nghĩ rằng bạn sẽ bỏ lỡ khả năng đó.

+2

Giải pháp "đơn giản" có hoạt động với BigNum hoặc Rational? Tôi có thể định nghĩa một thư viện toàn bộ các định lý toán học và có chúng được sử dụng bởi một trong hai, số nguyên, bignum hoặc hợp lý? –

+1

+1 cho cả việc thực hiện và lý do tại sao nó không nên được thực hiện như thế này. :-) –

+1

Bây giờ tôi được thông báo nhiều hơn về Scala, tôi thấy rằng một giải pháp khác tồn tại. Định nghĩa một lớp Num trừu tượng, các lớp con cho bất kỳ kiểu mong muốn nào, chuyển đổi ngầm từ kiểu mong muốn sang lớp con tương ứng, và làm cho pyth [A] chấp nhận "a" và "b" của A, cộng với ẩn từ A => Num [A ]. Bạn có nhớ thêm giải pháp này vào câu trả lời của bạn không? Tôi muốn chấp nhận nó, nhưng tôi muốn nó hoàn chỉnh hơn. –

0

Có một phương thức trong java.lang.Math:

public static double hypot (double x, double y) 

mà javadocs khẳng định:

Returns sqrt (x2 + y2) mà không tràn trung gian hoặc underflow.

nhìn vào src.zip, Math.hypot sử dụng StrictMath, mà là một phương pháp có nguồn gốc:

public static native double hypot(double x, double y); 
+1

Làm cách nào để sử dụng nó với Int hoặc với BigDecimal? Tôi không quan tâm đến việc tính toán giả thuyết, tôi quan tâm đến cách tôi làm toán học chung. –

+0

Tôi xin lỗi. Nó chỉ nên là một sidenote, sau đó. –

2

Một số suy nghĩ về Daniel câu trả lời:

Tôi đã experimented để khái quát Numeric để Real , sẽ phù hợp hơn với hàm này để cung cấp hàm sqrt. Điều này sẽ dẫn đến:

def pythagoras[T](a: T, b: T)(implicit n: Real[T]) = { 
    import n.mkNumericOps 
    (a*a + b*b).sqrt 
} 

Rất khó, nhưng có thể, để sử dụng các số theo các hàm chung như vậy.

def pythagoras[T](a: T, b: T)(sqrt: (T => T))(implicit n: Numeric[T]) = { 
    import n.mkNumericOps 
    implicit val fromInt = n.fromInt _ 

    //1 * sqrt(a*a + b*b) Not Possible! 
    sqrt(a*a + b*b) * 1 // Possible 
} 

Loại suy luận hoạt động tốt hơn nếu sqrt được truyền trong một danh sách tham số thứ hai.

Tham số ab sẽ được chuyển làm Đối tượng nhưng @specialized có thể khắc phục điều này. Không may là vẫn sẽ có một số chi phí trong các hoạt động toán học.

Bạn có thể gần như mà không cần nhập mkNumericOps. Tôi nhận được frustratringly close!

+0

Tất nhiên, 'n' có' một'. Và một 'số không'. –

+0

Một và không phải là số đủ cho bất kỳ ai! :) – retronym

+3

Tôi cũng muốn * e *, * π * và * i *, vì vậy tôi có thể thể hiện Danh tính của Euler. –

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