إعادة كتابة التعليمات البرمجية C# في#F
-
05-07-2019 - |
سؤال
مجرد العبث مع F# وكنت أحاول إنشاء الأساسية Lagrange الاستيفاء وظيفة على أساس هذا C# نسخة (نسخ من C++ ويكي الدخول):
double Lagrange(double[] pos, double[] val, double desiredPos)
{
double retVal = 0;
for (int i = 0; i < val.Length; ++i)
{
double weight = 1;
for (int j = 0; j < val.Length; ++j)
{
// The i-th term has to be skipped
if (j != i)
{
weight *= (desiredPos - pos[j]) / (pos[i] - pos[j]);
}
}
retVal += weight * val[i];
}
return retVal;
}
أفضل ما يمكن أن تأتي مع استخدام معرفتي محدودة F# وظيفية البرمجة:
let rec GetWeight desiredPos i j (pos : float[]) weight =
match i with
| i when j = pos.Length -> weight
| i when i = j -> GetWeight desiredPos i (j+1) pos weight
| i -> GetWeight desiredPos i (j+1) pos (weight * (desiredPos - pos.[j])/(pos.[i] - pos.[j]) )
let rec Lagrange (pos : float[]) (vals : float[]) desiredPos result counter =
match counter with
| counter when counter = pos.Length -> result
| counter -> Lagrange pos vals desiredPos (result + (GetWeight desiredPos counter 0 pos 1.0)* vals.[counter]) (counter+1)
يمكن للشخص تقديم أفضل/أرتب F# الإصدار على أساس نفس C# رمز ؟
المحلول
قابلة للطي على تسلسل هو وسيلة شائعة استبدال الحلقات مع المجمع.
let Lagrange(pos:_[], v:_[], desiredPos) =
seq {0 .. v.Length-1}
|> Seq.fold (fun retVal i ->
seq {for j in 0 .. pos.Length-1 do if i <> j then yield j}
|> Seq.fold (fun w j -> w * (desiredPos - pos.[j]) / (pos.[i] - pos.[j])) 1.0
|> (fun weight -> weight * v.[i] + retVal)) 0.0
نصائح أخرى
الجزء الذي يجعل حل عملي القبيح هو تخطي أنا أخذ عنصر ، مما يعني أن المؤشرات.اسحب إلى الاستخدام وظيفة كل قبيح مؤشر التعامل مع معزولة.أدعو لي RoundRobin.
let RoundRobin l = seq {
for i in {0..Seq.length l - 1} do
yield (Seq.nth i l, Seq.take i l |> Seq.append <| Seq.skip (i+1) l)
}
يمكن أن يكون الكثير أقبح إذا كنت تريد أن تنتج بكفاءة الإصدار ، على الرغم من.
لم أجد product
في Seq وحدة ، لذلك كتبت بلدي.
let prod (l : seq<float>) = Seq.reduce (*) l
الآن إنتاج مدونة بسيطة إلى حد ما:
let Lagrange pos value desiredPos = Seq.sum (seq {
for (v,(p,rest)) in Seq.zip value (RoundRobin pos) do
yield v * prod (seq { for p' in rest do yield (desiredPos - p') / (p - p') })
})
RoundRobin يضمن نقاط البيع[أنا] لا يتم تضمين مع بقية نقاط البيع في الحلقة الداخلية.تشمل val
مجموعة, أنا مضغوط مع المستديرة robinned pos
الصفيف.
الدرس هنا هو أن الفهرسة هو قبيح جدا في نمط وظيفي.كما اكتشفت خدعة رائعة: |> Seq.append <|
يعطيك أقحم الجملة من أجل إلحاق متواليات.ليس لطيفا كما ^
على الرغم من.
أعتقد أن هذا يعمل بشكل جيد كما حتمية كود:
let LagrangeI(pos:_[], v:_[], desiredPos) =
let mutable retVal = 0.0
for i in 0..v.Length-1 do
let mutable weight = 1.0
for j in 0..pos.Length-1 do
// The i-th term has to be skipped
if j <> i then
weight <- weight * (desiredPos - pos.[j]) / (pos.[i] - pos.[j])
retVal <- retVal + weight * v.[i]
retVal
ولكن إذا كنت تريد وظيفية بعض طيات (جنبا إلى جنب مع mapi منذ كنت غالبا ما تحتاج إلى تحمل مؤشرات على طول) تعمل بشكل جيد:
let LagrangeF(pos:_[], v:_[], desiredPos) =
v |> Seq.mapi (fun i x -> i, x)
|> Seq.fold (fun retVal (i,vi) ->
let weight =
pos |> Seq.mapi (fun j x -> j<>i, x)
|> Seq.fold (fun weight (ok, posj) ->
if ok then
weight * (desiredPos - posj) / (pos.[i] - posj)
else
weight) 1.0
retVal + weight * vi) 0.0
أنا لا أعرف الرياضيات هنا ، لذا تستخدم بعض القيم العشوائية لاختبار (أمل) ضمان لقد أخطئت في شيء:
let pos = [| 1.0; 2.0; 3.0 |]
let v = [|8.0; 4.0; 9.0 |]
printfn "%f" (LagrangeI(pos, v, 2.5)) // 5.375
printfn "%f" (LagrangeF(pos, v, 2.5)) // 5.375
وهنا غير حل العودية.انها قليلا غير تقليدي لأن خوارزمية يتطلب المؤشرات ، ولكن نأمل أن يظهر كيف و#'s وظائف يمكن أن تتكون:
let Lagrange (pos : float[]) (vals : float[]) desiredPos =
let weight pos desiredPos (i,v) =
let w = pos |> Array.mapi (fun j p -> j,p)
|> Array.filter (fun (j,p) -> i <> j)
|> Array.fold (fun acc (j,p) -> acc * (desiredPos - p)/(pos.[i] - p)) 1.
w * v
vals |> Array.mapi (fun i v -> i,v)
|> Array.sumBy (weight pos desiredPos)
let rec GetWeight desiredPos i j (pos : float[]) weight =
if j = pos.Length then weight
elif i = j then GetWeight desiredPos i (j+1) pos weight
else GetWeight desiredPos i (j+1) pos (weight * (desiredPos - pos.[j])/(pos.[i] - pos.[j]) )
let rec Lagrange (pos : float[]) (vals : float[]) desiredPos result counter =
if counter = pos.Length then result
else Lagrange pos vals desiredPos (result + (GetWeight desiredPos counter 0 pos 1.0)* vals.[counter]) (counter+1)
شخصيا أعتقد أنه بسيط إذا/أليف/آخر يبني انظر هنا أفضل بكثير من دون هذه النفقات العامة كما
match i with
|i when i=...
إذا كنت مجرد العبث ثم ها هي نسخة مشابهة براين يستخدم وظيفة التمشيط و tuple الأنابيب المشغل.
let Lagrange(pos:_[], v:_[], desiredPos) =
let foldi f state = Seq.mapi (fun i x -> i, x) >> Seq.fold f state
(0.0, v) ||> foldi (fun retVal (i, posi) ->
(1.0, pos) ||> foldi (fun weight (j, posj) ->
if j <> i then
(desiredPos - posj) / (posi - posj)
else
1.0)
|> (fun weight -> weight * posi + retVal))
محاولتي:
let Lagrange(p:_[], v, desiredPos) =
let Seq_multiply = Seq.fold (*) 1.0
let distance i j = if (i=j) then 1.0 else (desiredPos-p.[j])/(p.[i]-p.[j])
let weight i = p |> Seq.mapi (fun j _ -> distance i j) |> Seq_multiply
v |> Seq.mapi (fun i vi -> (weight i)*vi) |> Seq.sum
ريفاكتور بجعل الحلقة الداخلية وظيفة.ونحن أيضا يمكن أن تجعل القانون أكثر وضوحا و "المفهوم" من خلال تحديد بعض معنى وظائف.
أيضا, كتابة هذا يسلط الضوء على خطأ في رمز الأصلي (جميع المتغيرات الأخرى).المسافة وظيفة يجب أن يكون في الواقع:
let distance i j = if (p.[i]=p.[j]) then 1.0 else (desiredPos-p.[j])/(p.[i]-p.[j])
لتجنب العامة div عن طريق الخطأ صفر.وهذا يؤدي إلى عامة indexless الحل:
let Lagrange(p, v, desiredPos) =
let distance pi pj = if (pi=pj) then 1.0 else (desiredPos-pj)/(pi-pj)
let weight pi vi = p |> Seq.map (distance pi) |> Seq.fold (*) vi
Seq.map2 weight p v |> Seq.sum