Jest to dość proste do przetłumaczenia:
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)
Po pierwsze, spójrzmy na rubin - zwraca wartość, ale czasami drukuje komunikat o błędzie (jeśli podano niewłaściwy argument). Nie jest to zbyt zabawne, więc przyjmijmy z powrotem wartość Either String a
- gdzie zwrócimy Left String
z komunikatem o błędzie, jeśli zostanie podany niewłaściwy argument, a inaczej: Right a
.
Teraz możemy sprawdzić dwa przypadki u góry:
qn < 0 || 1 < qn = Left "Error: qn must be in [0,1]"
- jest to warunek błędu, gdy qn
jest poza zasięgiem.
qn == 0.5 = Right 0.0
- Czy kontrola ta Ruby qn == 0.5 and return * 0.0
Następna w kolejce, definiujemy w1
w kodzie Ruby. Ale zredukowaliśmy to kilka linii później, co nie jest bardzo rubinowe. Wartość, którą przechowujemy w w1
po raz pierwszy jest używana bezpośrednio w definicji w3
, więc dlaczego nie pominiemy jej przechowywania w w1
? Nie musimy nawet wykonywać kroku qn > 0.5 and w1 = 1.0 - w1
, ponieważ używamy produktu w1 * (1.0 - w1)
w definicji w3.
Pomijamy to wszystko i przechodzimy od razu do definicji w3 = negate . log $ 4 * qn * (1 - qn)
.
Następna jest definicja b
, która jest linią prostą z kodu ruby (składnia ruby dla literału tablicowego to składnia haskella dla listy).
Oto najtrudniejszy bit - definiujący ostateczną wartość w3
. Jaki kod Ruby robi w
w1 = b[0]
1.upto 10 do |i|
w1 += b[i] * w3**i;
end
jest to, co nazywa się fałd - zmniejszenie zbiór wartości (przechowywany w tablicy ruby) do pojedynczej wartości. Możemy przekształcić to bardziej użyciu Array#reduce
funkcjonalnie (ale nadal w Ruby):
w1 = b.zip(0..10).reduce(0) do |accum, (bval,i)|
accum + bval * w3^i
end
Uwaga jak podsunąłem b[0]
do pętli przy użyciu tożsamości b[0] == b[0] * w3^0
.
Teraz możemy portu to bezpośrednio do Haskell, ale to trochę brzydki
w1 = foldl 0 (\accum (bval,i) -> accum + bval * w3**i) $ zip b [0..10]
Zamiast Złamałem go na kilka etapów - Po pierwsze, tak naprawdę nie potrzebują i
, po prostu potrzebne moce w3
(począwszy od w3^0 == 1
), więc obliczyć te z iterate (*w3) 1
.
Następnie, zamiast skompresować je parami z elementami b, ostatecznie potrzebujemy ich produktów, abyśmy mogli je zamapować na produkty każdej pary przy użyciu zipWith (*) b
.
Teraz nasza funkcja składania jest naprawdę łatwa - wystarczy podsumować produkty, które możemy wykonać za pomocą sum
.
Wreszcie możemy zdecydować, czy powrócić plus lub minus sqrt (w1 * w3)
, zależnie od tego czy qn
jest większa lub mniejsza niż 0,5 (my już wiem, że to nie jest równe). Więc zamiast obliczać pierwiastek kwadratowy w dwóch oddzielnych miejscach, jak w kodzie ruby, przeliczyłem go jeden raz i pomnożyłem przez +1
lub -1
według oznaczenia (signum
).
Ja naprawdę nic nie wiem o statystykach: P. Czy wiesz, która z tych funkcji jest odpowiednikiem pnormaldist? –
Nie sądzę, że żadna z tych funkcji jest dokładnie tym, czego potrzebujesz. Potrzebujesz odwrotności funkcji erf, jeśli się nie mylę. – augustss