سؤال

Is there a more idiomatic way how to do pattern matching on records? My code just doesn't seem right.

type Period = AM | PM

type TimeOfDay = {hours : int; minutes : int; p : Period}

let before (tod1 : TimeOfDay, tod2 : TimeOfDay) =
   match tod1, tod2 with
   | {hours = h1; minutes = m1; p = AM}, {hours = h2; minutes = m2; p = AM} -> (h1, m1) < (h2, m2)
   | {hours = h1; minutes = m1; p = PM}, {hours = h2; minutes = m2; p = PM} -> (h1, m1) < (h2, m2)
   | {hours = _; minutes = _; p = AM}, {hours = _; minutes = _; p = PM} -> true
   | {hours = _; minutes = _; p = PM}, {hours = _; minutes = _; p = AM} -> false
هل كانت مفيدة؟

المحلول

You can do a slight improvement as you don't need to show unneeded patterns to produce the following

let before (tod1 : TimeOfDay, tod2 : TimeOfDay) =
   match tod1, tod2 with
   | {hours = h1; minutes = m1; p = AM}, {hours = h2; minutes = m2; p = AM} -> (h1, m1) < (h2, m2)
   | {hours = h1; minutes = m1; p = PM}, {hours = h2; minutes = m2; p = PM} -> (h1, m1) < (h2, m2)
   | { p = AM}, {p = PM} -> true
   | { p = PM}, {p = AM} -> false

Next, you could define an Active Pattern to deconstruct the type into a Tuple as follows

let (|TIME|) (t:TimeOfDay) = t.hours,t.minutes,t.p

let before (tod1 : TimeOfDay, tod2 : TimeOfDay) =
   match tod1, tod2 with
   | TIME(h1,m1,AM), TIME(h2,m2,PM) -> (h1, m1) < (h2, m2)
   | TIME(h1,m1,PM), TIME(h2,m2,PM) -> (h1, m1) < (h2, m2)
   | { p = AM}, {p = PM} -> true
   | { p = PM}, {p = AM} -> false

نصائح أخرى

you can remove the properties that you dont need on the match, use "guards" (when clauses) to check if both pproperties are equals, and create a local function to simplify tuple comparasion.

let before (tod1: TimeOfDay, tod2: TimeOfDay) =
   let getTime tod = (tod.hours, tod.minutes)
   match tod1, tod2 with
   | { p = x }, { p = y } when x = y -> getTime tod1 < getTime tod2
   | { p = AM }, _ -> true
   | _ -> false

or to remove the last pattern just do

let before (tod1: TimeOfDay, tod2: TimeOfDay) =
   let getTime tod = (tod.hours, tod.minutes)
   match tod1, tod2 with
   | { p = x }, { p = y } when x = y -> getTime tod1 < getTime tod2
   | { p = x }, _ -> x = AM
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top