سؤال

وأريد أن استخراج عنصر واحد من سلسلة 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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top