này là khá dễ dàng để dịch:
module PNormalDist where
pnormaldist :: (Ord a, Floating a) => a -> Either String a
pnormaldist qn
| qn < 0 || 1 < qn = Left "Error: qn must be in [0,1]"
| qn == 0.5 = Right 0.0
| otherwise = Right $
let w3 = negate . log $ 4 * qn * (1 - qn)
b = [ 1.570796288, 0.03706987906, -0.8364353589e-3,
-0.2250947176e-3, 0.6841218299e-5, 0.5824238515e-5,
-0.104527497e-5, 0.8360937017e-7, -0.3231081277e-8,
0.3657763036e-10, 0.6936233982e-12]
w1 = sum . zipWith (*) b $ iterate (*w3) 1
in (signum $ qn - 0.5) * sqrt (w1 * w3)
Trước hết, chúng ta hãy nhìn vào ruby - nó trả về một giá trị, nhưng đôi khi nó in một thông báo lỗi (khi đưa ra một lập luận không đúng cách). Đây không phải là rất kiêu ngạo, do đó, chúng ta hãy có giá trị trả về của chúng tôi là Either String a
- nơi chúng tôi sẽ trả về một Left String
với thông báo lỗi nếu đưa ra một đối số không đúng và Right a
nếu không.
Bây giờ chúng tôi kiểm tra hai trường hợp ở đầu trang:
qn < 0 || 1 < qn = Left "Error: qn must be in [0,1]"
- đây là điều kiện lỗi, khi qn
là ra khỏi phạm vi.
qn == 0.5 = Right 0.0
- đây là việc kiểm tra ruby qn == 0.5 and return * 0.0
Tiếp theo, chúng ta định nghĩa w1
trong mã ruby. Nhưng chúng tôi xác định lại nó một vài dòng sau đó, mà không phải là rất rubyish. Giá trị mà chúng tôi lưu trữ trong w1
lần đầu tiên được sử dụng ngay lập tức theo định nghĩa của w3
, vậy tại sao chúng ta không bỏ qua lưu trữ nó trong w1
? Chúng tôi thậm chí không cần thực hiện bước qn > 0.5 and w1 = 1.0 - w1
, vì chúng tôi sử dụng sản phẩm w1 * (1.0 - w1)
trong định nghĩa của w3.
Vì vậy, chúng tôi bỏ qua tất cả điều đó và chuyển thẳng đến định nghĩa w3 = negate . log $ 4 * qn * (1 - qn)
.
Tiếp theo là định nghĩa của b
, là thang máy thẳng từ mã ruby (cú pháp của ruby cho mảng chữ là cú pháp của haskell cho danh sách).
Đây là bit phức tạp nhất - xác định giá trị cuối cùng của w3
. Mã ruby làm gì trong
w1 = b[0]
1.upto 10 do |i|
w1 += b[i] * w3**i;
end
Được gọi là nếp gấp - giảm một tập hợp các giá trị (được lưu trữ trong một mảng ruby) thành một giá trị duy nhất. Chúng ta có thể xác định lại chức năng này hơn (nhưng vẫn trong ruby) sử dụng Array#reduce
:
w1 = b.zip(0..10).reduce(0) do |accum, (bval,i)|
accum + bval * w3^i
end
Lưu ý làm thế nào tôi đẩy b[0]
vào vòng lặp, bằng cách sử dụng danh tính b[0] == b[0] * w3^0
.
Bây giờ chúng ta có thể cổng này trực tiếp đến Haskell, nhưng đó là một chút xấu xí
w1 = foldl 0 (\accum (bval,i) -> accum + bval * w3**i) $ zip b [0..10]
Thay vào đó, tôi chia ra thành nhiều bước - trước hết, chúng ta không thực sự cần i
, chúng ta chỉ cần quyền hạn của w3
(bắt đầu từ w3^0 == 1
), do đó, hãy tính toán những người có iterate (*w3) 1
.
Sau đó, thay vì nén chúng thành từng cặp với các phần tử của b, cuối cùng chúng ta chỉ cần các sản phẩm của chúng, vì vậy chúng tôi có thể nén chúng thành các sản phẩm của mỗi cặp sử dụng zipWith (*) b
.
Bây giờ chức năng gấp của chúng tôi thực sự dễ dàng - chúng tôi chỉ cần tổng hợp các sản phẩm mà chúng tôi có thể thực hiện bằng cách sử dụng sum
.
Cuối cùng, chúng tôi quyết định có trả lại dấu cộng hay trừ sqrt (w1 * w3)
, theo số liệu qn
lớn hơn hoặc nhỏ hơn 0,5 (chúng tôi đã biết không bằng). Vì vậy, thay vì tính căn bậc hai ở hai vị trí riêng biệt như trong mã ruby, Tôi đã tính nó một lần và nhân nó theo +1
hoặc -1
theo ký hiệu qn - 0.5
(signum
just returns the sign of a value).
Tôi thực sự không biết gì về thống kê: P. Bạn có biết những chức năng nào tương đương với pnormaldist không? –
Tôi không nghĩ rằng bất kỳ chức năng nào là chính xác những gì bạn cần. Bạn cần nghịch đảo của hàm erf, nếu tôi không nhầm. – augustss