سؤال

أنا أتعلم كيفية الكتابة بدون نقاط وكانت الأمور تسير على ما يرام حتى واجهت هذا:

let rec toSeq (reader : SqlDataReader) toItem = seq {
        if reader.Read()
        then
            yield toItem reader
            yield! toSeq reader toItem
        else
            yield! Seq.empty }

وهذا:

let execute sql init toItem = 
    seq {
        use command = new SqlCommand(sql)
        command |> init
        use connection = new SqlConnection("")
        connection.Open()
        command.Connection <- connection
        use reader = command.ExecuteReader()
        yield! toSeq reader toItem } |> Seq.toList

ليس لدي أي فكرة عن كيفية تجاوز منشئ التسلسل ...هل هذا ممكن حتى؟

أحتاج إلى التأكد من أن الاستخدامات لا تزال تعمل بشكل صحيح أيضًا.

لعِلمِكَ:أعلم أنه قد يبدو من غير المجدي استخدام البرمجة الخالية من النقاط هنا.فقط افهم أن هذا تمرين تعليمي بالنسبة لي.

تحديث:هذه هي محاولتي الأولى في الوظيفة الثانية.اضطررت إلى إزالة مراجع التسلسل بالرغم من ذلك:

let ExecuteReader (command : SqlCommand) (connection : SqlConnection) = 
    command.Connection <- connection
    command.ExecuteReader()

let c x y =  ((>>) x) << ((<<) << y)

let (>>|) = c

let execute =
    ExecuteReader 
    >>| ((>>) toSeq) (flip using) 
    >>| using 
    >>| using
هل كانت مفيدة؟

المحلول

حسنًا، كما أشرنا سابقًا في التعليقات، فإن كتابة التعليمات البرمجية الحتمية بأسلوب خالٍ من النقاط ليست فكرة جيدة على الإطلاق.ليس فقط أنه لا يفعل ذلك أكثر قابلية للقراءة, ، لكنه يجعلها أكثر معرض للخطأ, لأنه من الصعب التفكير في التنفيذ.حتى مع الكود الوظيفي، غالبًا ما أجد النمط غير الخالي من النقاط أكثر قابلية للقراءة (وليس أطول بكثير).

على أية حال، بما أنك سألت، فإليك بعض الخطوات التي يمكنك اتخاذها - لاحظ أن هذا في الواقع تمرين على التشويش الوظيفي.ليس شيئًا تريد القيام به على الإطلاق.

سيتم إلغاء تحويل رمز الوظيفة الأولى إلى شيء مثل هذا (سيكون هذا أقل كفاءة لأنه تم تحسين تعبيرات التسلسل):

let rec toSeq (reader : SqlDataReader) toItem = Seq.delay (fun () ->
  if reader.Read() then 
    Seq.concat [ Seq.singleton (toItem  reader); toSeq reader toItem ]
  else 
    Seq.empty)

حتى في الأسلوب الخالي من النقاط، مازلت بحاجة إلى Seq.delay للتأكد من أنك تقوم بتنفيذ التسلسل بتكاسل.ومع ذلك، يمكنك تحديد وظيفة مختلفة قليلاً تتيح لك كتابة التعليمات البرمجية بأسلوب خالٍ من النقاط.

// Returns a delayed sequence generated by passing inputs to 'f'
let delayArgs f args = Seq.delay (fun () -> f args)

let rec toSeq2 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> = 
  delayArgs (fun (reader, toItem) ->
    if reader.Read() then 
      Seq.concat [ Seq.singleton (toItem  reader); toSeq2 (reader, toItem) ]
    else 
      Seq.empty)

الآن، نص الدالة هو مجرد وظيفة تم تمريرها إلى delayArgs وظيفة.يمكننا محاولة إنشاء هذه الوظيفة من وظائف أخرى بأسلوب خالٍ من النقاط.ال if بالرغم من ذلك، فهو أمر صعب، لذلك نستبدله بمركب يأخذ ثلاث وظائف (ويمرر نفس المدخلات إلى كل منهم):

let cond c t f inp = if c inp then t inp else f inp

let rec toSeq3 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> = 
  delayArgs (cond (fun (reader, _) -> reader.Read()) 
                  (fun (reader, toItem) -> 
                     Seq.concat [ Seq.singleton (toItem  reader); 
                                  toSeq3 (reader, toItem) ])
                  (fun _ -> Seq.empty))

لا يمكنك التعامل مع استدعاءات الأعضاء بأسلوب خالٍ من النقاط، لذلك تحتاج إلى تحديد الوظيفة التي تستدعي Read.ثم يمكنك أيضًا استخدام دالة تُرجع دالة ثابتة (لتجنب تضارب الأسماء، المسماة konst):

let read (reader:SqlDataReader) = reader.Read()
let konst v _ = v

باستخدام الاثنين، يمكنك تحويل الوسيطة الأخيرة والثانية إلى نمط خالٍ من النقاط:

let rec toSeq4 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> = 
  delayArgs (cond (fst >> read) 
                  (fun (reader, toItem) -> 
                     Seq.concat [ Seq.singleton (toItem  reader); 
                                  toSeq4 (reader, toItem) ])
                  (konst Seq.empty))

باستخدام بعض المجموعات الأكثر جنونًا (uncurry موجود في هاسكل.ال list2 يمكن أيضًا كتابة combinator بأسلوب خالٍ من النقاط، ولكن أعتقد أنك فهمت الفكرة):

let list2 f g inp = List.Cons(f inp, List.Cons(g inp, []))
let uncurry f (a, b) = f a b

let rec toSeq5 : (SqlDataReader * (SqlDataReader -> int)) -> seq<int> = 
  delayArgs (cond (fst >> read) 
                  (list2 ((uncurry (|>)) >> Seq.singleton) toSeq5 >> Seq.concat)
                  (konst Seq.empty))

هذا لا يجمع تماما، لأن toSeq5 يتم تقييمها كجزء من تعريفها، ولكن إذا قمت بإدخال بعض وظائف التأخير، فقد تفعل في الواقع نفس الشيء الذي فعلته في الأصل.

ملخص - لم أعد أعرف ما إذا كان الكود أعلاه صحيحًا وكيف يتم تقييمه (قد يقيم القارئ بفارغ الصبر، أو يحتوي على نوع آخر من الأخطاء).إنه يقوم بفحص النوع، لذلك ربما لا يكون بعيدًا عن العمل.الكود غير قابل للقراءة تمامًا، ويصعب تصحيحه ومن المستحيل تعديله.

فكر في هذا كمثال متطرف لرمز مجنون خالٍ من النقاط يمكنك كتابته في F#.من الناحية العملية، أعتقد أنه يجب استخدام الأسلوب الخالي من النقاط فقط في الأشياء التافهة مثل استخدام الوظائف >>.بمجرد أن تحتاج إلى تحديد combinators مثل uncurry أو konst, ، سيكون من الصعب جدًا على الأشخاص قراءة التعليمات البرمجية الخاصة بك.

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