استخراج عنصر واحد من القائمة في F #
-
05-07-2019 - |
سؤال
وأريد أن استخراج عنصر واحد من سلسلة F في #، أو إعطاء خطأ إذا كان هناك شيء أو أكثر من واحد. ما هي أفضل طريقة للقيام بذلك؟
ولدي حاليا
let element = data |> (Seq.filter (function | RawXml.Property (x) -> false | _ -> true))
|> List.of_seq
|> (function head :: [] -> head | head :: tail -> failwith("Too many elements.") | [] -> failwith("Empty sequence"))
|> (fun x -> match x with MyElement (data) -> x | _ -> failwith("Bad element."))
ويبدو للعمل، ولكن هل هو حقا أفضل وسيلة؟
وتحرير: وكما أشير في الاتجاه الصحيح، خطرت لي ما يلي:
let element = data |> (Seq.filter (function | RawXml.Property (x) -> false | _ -> true))
|> (fun s -> if Seq.length s <> 1 then failwith("The sequence must have exactly one item") else s)
|> Seq.hd
|> (fun x -> match x with MyElement (_) -> x | _ -> failwith("Bad element."))
وأنا أعتقد أنها أجمل قليلا.
المحلول
وتسلسل لديه وظيفة البحث.
val find : ('a -> bool) -> seq<'a> -> 'a
ولكن إذا كنت ترغب في التأكد من أن يليها ديه عنصر واحد فقط، ثم تقوم بعمل Seq.filter، ثم تأخذ في طول بعد تصفية وتأكد من أنه يساوي واحد، ومن ثم اتخاذ الرأس. كل ذلك في تسلسل، لا حاجة لتحويله إلى القائمة.
وتحرير:
على الجانب علما، I <م> كان م> الذهاب تشير إلى التحقق من أن <م> ذيل م> من نتيجة فارغ (O (1)، بدلا من استخدام length
وظيفة (O (ن) ). الذيل ليست جزءا من بعدها، ولكن أعتقد أنك يمكن أن تعمل من وسيلة جيدة لمحاكاة هذه الوظيفة.
نصائح أخرى
وذلك في أسلوب وظائف تسلسل مستوى القائمة
#light
let findOneAndOnlyOne f (ie : seq<'a>) =
use e = ie.GetEnumerator()
let mutable res = None
while (e.MoveNext()) do
if f e.Current then
match res with
| None -> res <- Some e.Current
| _ -> invalid_arg "there is more than one match"
done;
match res with
| None -> invalid_arg "no match"
| _ -> res.Value
هل يمكن أن تفعل التنفيذ النقي لكنها في نهاية المطاف القفز من خلال الأطواق لتكون صحيحة وفعالة (تنتهي بسرعة على المباراة الثانية يدعو حقا للعلم قائلا "لقد وجدت أنه بالفعل ')
استخدم هذا:
> let only s =
if not(Seq.isEmpty s) && Seq.isEmpty(Seq.skip 1 s) then
Seq.hd s
else
raise(System.ArgumentException "only");;
val only : seq<'a> -> 'a
ما هو الخطأ في استخدام وظيفة مكتبة موجودة؟
let single f xs = System.Linq.Enumerable.Single(xs, System.Func<_,_>(f))
[1;2;3] |> single ((=) 4)
وبلدي اثنين سنتا ... وهذا يعمل مع نوع الخيار بحيث يمكنني استخدامها في بلدي مخصص ربما الكائن الدقيق الاحادي الخلية. يمكن تعديلها من السهل جدا على الرغم من أن العمل مع استثناءات بدلا
let Single (items : seq<'a>) =
let single (e : IEnumerator<'a>) =
if e.MoveNext () then
if e.MoveNext () then
raise(InvalidOperationException "more than one, expecting one")
else
Some e.Current
else
None
use e = items.GetEnumerator ()
e |> single
والجواب التحديث سيكون لاستخدام Seq.exactlyOne الذي يثير ArgumentException