اكتب نقطة تعبير تسلسل f# مجانًا
سؤال
أنا أتعلم كيفية الكتابة بدون نقاط وكانت الأمور تسير على ما يرام حتى واجهت هذا:
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
, ، سيكون من الصعب جدًا على الأشخاص قراءة التعليمات البرمجية الخاصة بك.